MySQL 事务隔离是后端面试里的经典问题。很多候选人会背读未提交、读已提交、可重复读、串行化,也能说脏读、不可重复读、幻读。但面试官继续问:为什么同一个事务里两次查询结果一样?普通查询和更新查询看到的数据为什么不同?你项目里什么时候需要事务?这时就容易绕。
这类题的关键,是把隔离级别和具体读写场景连起来。
先用问题解释隔离级别
脏读是读到了别人还没提交的数据;不可重复读是同一个事务里两次读同一行,结果变了;幻读是同一个事务里按条件查一批数据,后来出现了新行。背定义不难,难的是理解数据库为什么要在并发和性能之间做取舍。
隔离越强,并发性能通常越低。项目里不一定追求最强隔离,而是要看业务能否接受某些短暂不一致。
MVCC 可以理解为版本快照
MVCC 可以用中文理解成多版本并发控制。它的核心思路是:读操作尽量不阻塞写操作,数据库为数据保留多个版本,普通查询根据事务开始时的快照判断该看到哪个版本。这样很多查询不用加锁,也能获得相对一致的结果。
面试里不需要一开始就堆内部字段,可以先讲效果:在可重复读隔离级别下,一个事务里的普通查询可能一直看到事务开始时的快照,所以别人提交的新数据不一定立刻被它看到。
快照读和当前读要区分
普通查询通常是快照读,也就是读一个符合规则的历史版本。更新、删除、加锁查询通常是当前读,也就是要读最新数据并加锁,因为它要修改真实记录。很多面试追问就卡在这里:为什么普通查询没看到别人的提交,但更新时又会影响最新数据?
可以这样解释:查询只是看数据,可以用快照保证一致视图;更新要改变数据,必须面对当前最新状态,否则会覆盖别人已经提交的修改。
项目里怎么回答事务
项目回答不要只说“用了事务”。要讲事务边界。比如订单创建和状态初始化必须一起成功,应该放在同一个事务里;发通知、写日志、同步报表不应该拖长主事务,可以异步处理;外部接口调用失败不能靠数据库回滚解决,需要补偿。
如果被问事务隔离,可以结合场景说:库存扣减不能只依赖先查后改,要在更新条件里校验库存数量,或者通过锁和状态限制并发修改。查询列表允许短暂旧数据,但支付状态这类核心结果要以当前数据库状态为准。
事务隔离和 MVCC 的面试高分点,不是背术语,而是能说明读、写、锁和业务一致性之间的关系。
事务回答里的边界判断
事务不是把代码都包起来就安全。面试里要讲清楚哪些动作需要原子性,哪些动作不应该放进事务,哪些一致性要靠业务条件和幂等兜住。
| 场景 | 事务里应该包含 | 不建议包含 | 原因 |
|---|---|---|---|
| 创建订单 | 订单主记录和必要明细 | 慢速第三方调用 | 避免长事务占锁 |
| 扣库存 | 条件更新和流水记录 | 先查后长时间计算 | 减少并发窗口 |
| 修改状态 | 原状态限制和更新时间 | 无条件覆盖 | 防止状态回退或跳跃 |
| 发送通知 | 可靠事件记录 | 直接在事务里发远程消息 | 外部调用无法随事务回滚 |
讲 MVCC 时可以补一句:快照读提升读并发,但更新仍要面对最新数据。项目里真正重要的是把业务约束写进更新条件,而不是只依赖“开了事务”。