很多后端工程师对“事务隔离级别”停留在理论层面,但真正写业务时,数据错乱、更新丢失、脏读、幻读等问题仍然频繁出现。
这篇文章我从实战角度把四大隔离级别讲清楚,并结合 MySQL InnoDB 的 MVCC 展示真实行为。
一、四大隔离级别不是死记硬背
- Read Uncommitted:能读别人未提交的(脏读)
- Read Committed:每次读都是最新版本
- Repeatable Read:同一事务里多次读结果一致(InnoDB 默认)
- Serializable:完全串行化,最安全但性能差
但这只是表象。真正关键点是:它们如何使用 MVCC(多版本控制)实现?
二、InnoDB 的 MVCC 做了什么?
每一行记录有两个隐藏字段:
trx_id(创建它的事务ID)
roll_pointer(指向 undo log)
当你执行:
SELECT * FROM user WHERE id=1;
实际上数据库做的是:
- 找到最新版本
- 回溯 undo log 找到“该快照下可见的版本”
- 返回给你
这就是为什么同一行数据能被多个事务读取不同版本。
三、Repeatable Read 为什么能避免幻读?
很多人以为 RR 会产生幻读,其实 InnoDB 的 RR 是不会幻读的。
因为两个机制:
- MVCC 提供快照读
- Next-Key Lock 锁住区间写
比如:
SELECT * FROM orders WHERE amount > 100;
另一个事务不能插入 amount > 100 的新记录,因为 Next-Key Lock 锁住了范围。
四、常见坑:更新语句不是快照读
例如:
UPDATE user SET score = score + 1 WHERE id = 1;
UPDATE 是 当前读 (Current Read) ,会加锁并读最新数据。
这经常造成你的事务逻辑“看起来不一致”。
五、如何排查事务隔离问题?
我总结了一个 Checklist:
- 是否使用了快照读?(select)
- 是否使用了当前读?(update/delete/for update)
- 是否遗漏索引导致锁范围扩大?
- 是否使用默认 RR 但预期是 RC?
- 是否读写混用导致版本冲突?
六、总结
事务隔离不是死记,而是 理解 MVCC 行为:
- RC 保证每次读都是当前版本
- RR 保证同一事务读一致性 + 无幻读
- 当前读与锁行为影响业务结果
- UPDATE 永远是当前读
理解了这套机制,90% 的“诡异读写问题”都能解释清楚。