MySQL 事务题经常从隔离级别开始,但真正的项目追问往往落在“更新”上。面试官可能问:为什么事务里普通查询看到旧数据,但更新又会影响最新数据?库存扣减为什么不能先查再改?状态流转并发时怎么保证不乱?
快照读和当前读的差异
普通查询通常是快照读,也就是读一个一致性视图。它适合让事务内多次查询保持稳定,不必阻塞其他事务提交。当前读则面向最新记录,常见于更新、删除、加锁查询,因为这些操作要改变真实数据,不能只看历史版本。
用面试里的话说:查询可以“看过去”,更新必须“面对现在”。
为什么先查再改不稳
库存扣减是典型例子。低分回答是:先查库存大于 0,再扣减。问题是并发下多个请求可能都查到库存够,然后一起扣,最后超卖。更稳的做法是把条件放进更新语句里,比如只有库存大于等于购买数量时才扣减。更新本身会面对当前数据,并通过锁保护这一行。
订单状态流转也类似。不要先查状态再随意更新,而是把当前状态作为更新条件:只有待支付才能改成已支付,只有待发货才能改成已发货。这样即使重复请求或并发请求过来,也不会把状态改乱。
锁不是越多越好
面试官可能继续问行锁、间隙锁。回答时不要展开成纯概念堆砌。可以说:命中索引的精确更新通常能把锁控制在较小范围;如果条件没有命中合适索引,可能扫描更多记录,锁范围和性能风险都会上升。因此事务里的查询条件和索引设计也会影响并发能力。
项目回答示例
可以这样说:我在订单或库存场景里,不会只依赖先查再改,而是把业务条件放进更新语句,让数据库在当前最新状态下判断是否允许修改。事务只包住核心状态变更,避免在事务里做耗时外部调用。上线后重点看更新失败原因、锁等待、慢查询和异常状态数量。
这类回答能把 MVCC、当前读和项目一致性串起来,面试官更容易判断你理解数据库并发。
面试里容易丢分的地方,是把事务隔离说成“数据库会自动保证一切”。数据库能提供并发控制,但业务条件仍然要写对。比如更新库存时没有把库存数量放进更新条件,或者更新订单状态时没有限制原状态,即使开了事务,也可能出现业务上的错误状态。高质量回答应该同时包含数据库机制和业务约束。
把更新语句讲成并发保护
当前读和锁的价值,最终要落在“这条更新语句能不能保护业务状态”。面试官通常不是想听定义,而是想看你能不能避免超卖、重复支付、状态跳转错乱这些真实问题。
| 场景 | 不稳写法 | 更稳写法 | 原因 |
|---|---|---|---|
| 库存扣减 | 先查库存再更新 | update 时带库存充足条件 | 判断和修改合在一次当前读里 |
| 订单支付 | 先查状态再改已支付 | update 时限制原状态为待支付 | 防止重复回调改乱状态 |
| 任务领取 | 查询一条待处理再更新 | 加锁读取或状态条件更新 | 避免多个 worker 抢同一条 |
| 余额变更 | 应用内计算后覆盖写 | 使用增量更新和流水记录 | 保留审计并减少覆盖风险 |
更进一步,可以说明索引也会影响锁范围。业务条件命中合适索引,数据库更容易锁住精确记录;条件写得模糊,锁等待和扫描范围都会扩大。这样事务题就不再只是隔离级别背诵。