数据库一致性不是“用了事务就不会出错”。事务保证一组数据库操作的边界,锁和条件更新解决并发冲突,复制负责可用性与读扩展;它们解决的是不同问题。

ACID 落到业务里是什么

特性 扣库存场景中的含义
原子性 创建订单和扣库存要么都成功,要么都回滚
一致性 库存不能被业务操作变成负数
隔离性 并发事务不应读到不允许看见的中间状态
持久性 提交成功的数据在故障恢复后仍可找回

事务只覆盖同一个数据库事务中的操作。发短信、调用支付接口或向另一个数据库写入,不能靠本地事务自动回滚。

最直接的原子扣减

1
2
3
4
UPDATE products
SET stock = stock - 1
WHERE id = 42
AND stock > 0;

应用必须检查受影响行数。为 0 表示商品不存在或库存不足。这种条件更新通常比“先查库存再更新”更安全,因为判断和修改在一条 SQL 中完成。

悲观锁与乐观锁

悲观锁先锁住目标行:

1
2
3
4
5
6
7
8
9
START TRANSACTION;

SELECT stock
FROM products
WHERE id = 42
FOR UPDATE;

UPDATE products SET stock = stock - 1 WHERE id = 42;
COMMIT;

乐观锁用版本号判断数据是否已变化:

1
2
3
4
5
6
UPDATE products
SET stock = stock - 1,
version = version + 1
WHERE id = 42
AND version = 7
AND stock > 0;

冲突少且允许重试时,乐观锁更轻;冲突频繁且必须串行修改时,悲观锁更直观。两者都需要明确失败后的业务处理。

死锁为什么发生

事务 A 先锁商品 1 再等商品 2,事务 B 先锁商品 2 再等商品 1,就形成循环等待。常用预防方式:

  • 按固定顺序访问资源。
  • 缩短事务,不在持锁期间请求远程接口。
  • 给查询条件建立合适索引,减少锁定范围。
  • 捕获死锁错误并做有上限的重试。

主从复制解决什么

1
应用写主库 -> 主库记录 Binlog -> 从库拉取并重放 -> 应用读取从库

异步复制存在延迟,因此“刚写完立刻读从库”可能读不到新数据。关键读可以短时间走主库,或者通过一致性标记、复制位点等机制确认从库已经追上。

主库故障后的切换

1
2
3
4
5
6
7
监控确认主库不可用
-> 选择数据最新且健康的从库
-> 提升为新主库
-> 修改服务发现或连接配置
-> 让其他从库追随新主库
-> 校验数据与业务写入
-> 隔离旧主库,防止双主写入

自动切换不能消除数据丢失风险。是否允许丢失最后几秒写入,取决于复制模式、故障时副本进度和业务恢复目标。

延伸阅读

站内搜索

没有找到内容!