深入理解数据库事务隔离级别:为什么你的读写不一致?

38 阅读2分钟

很多后端工程师对“事务隔离级别”停留在理论层面,但真正写业务时,数据错乱、更新丢失、脏读、幻读等问题仍然频繁出现。
这篇文章我从实战角度把四大隔离级别讲清楚,并结合 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;

实际上数据库做的是:

  1. 找到最新版本
  2. 回溯 undo log 找到“该快照下可见的版本”
  3. 返回给你

这就是为什么同一行数据能被多个事务读取不同版本。


三、Repeatable Read 为什么能避免幻读?

很多人以为 RR 会产生幻读,其实 InnoDB 的 RR 是不会幻读的

因为两个机制:

  1. MVCC 提供快照读
  2. 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% 的“诡异读写问题”都能解释清楚。