Py学习  »  DATABASE

明明 MVCC 已经实现了无锁读,为什么 MySQL 还需要四种隔离级别?

IT服务圈儿 • 1 月前 • 129 次点击  
来源丨经授权转自 程序员小富(ID:chengxy-nds)
作者丨小富

MVCC 多版本并发控制,读不加锁,读写互不阻塞。

既然读都不用加锁了,数据库的并发问题不就解决了吗?那为什么 MySQL 还要搞出 READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、 SERIALIZABLE 四种隔离级别?MVCC 一把梭不就完了?


01
MVCC 到底做了什么

InnoDB 的每一行数据都有两个隐藏字段:

  • DB_TRX_ID:最后修改这行数据的事务 ID

  • DB_ROLL_PTR:指向 undo log 中这行的上一个版本

每次 UPDATE 一行数据,旧版本不会被覆盖,而是写到 undo log 里,通过指针串成一条版本链:

图片

当一个事务执行 SELECT 时,InnoDB 不是直接读当前版本,而是生成一个叫 ReadView 的快照,用它来判断版本链上的哪个版本对当前事务可见。

ReadView 里记录了当前所有活跃(还没提交)的事务 ID 列表。判断规则简单粗暴:

  • 这个版本的 trx_id 在我事务开始前就已经提交了 → 可见

  • 这个版本的 trx_id 是我之后才开始的事务 → 不可见,沿着版本链往下找

  • 这个版本的 trx_id 还在活跃列表里(没提交) →  不可见,继续往下找

到这里看起来一切都很完美:读的时候读历史版本,写的时候写当前版本,读写完全不冲突。


02
那为什么还需要隔离级别?

因为我应该看到哪个版本这个问题,不同业务场景的答案不一样。

MVCC 只是提供了一种读历史版本的机制,但具体读哪个历史版本,取决于 ReadView 的创建策略。而这个策略,就是隔离级别在控制的东西。

READ COMMITTED

每次 SELECT 创建新 ReadView

图片

RC 级别下,每次 SELECT 都重新拍一张快照。如果两次 SELECT 之间有其他事务提交了修改,第二次读就能看到新数据,这就是所谓的不可重复读,就是同一个事务里两次读同一行,结果不一样。

REPEATABLE READ

整个事务只创建一次 ReadView

图片

RR 级别下,事务第一次 SELECT 的时候创建 ReadView,后续所有 SELECT 都复用这同一个快照。不管外面的世界怎么变,你在这个事务里看到的数据永远是一致的。

RC 和 RR 用的是完全一样的 MVCC 机制,区别仅仅在于 ReadView 什么时候创建。一个是每次读都重新拍快照,一个是整个事务只拍一次。就这一个时机差异,造成了两种截然不同的隔离效果。


03
READ UNCOMMITTED 为什么不用 MVCC?

READ UNCOMMITTED 的语义是,可以读到其他事务还没提交的修改。

既然连未提交的都能读,那直接读版本链最上面的当前版本就完事了,根本不需要创建 ReadView 去过滤。MVCC 的意义在于屏蔽不该看到的版本,如果你什么都想看,那 MVCC 就是多此一举。

所以 READ UNCOMMITTED 不是"不能用 MVCC",而是用了也没意义。


04
SERIALIZABLE 为什么也不用 MVCC?

SERIALIZABLE 走的是另一个极端,所有读操作都加共享锁。

读也加锁意味着读写之间会互相阻塞,跟 MVCC 的设计目标(读写不阻塞)完全背道而驰。SERIALIZABLE 放弃了 MVCC 的并发优势,换来的是最强的一致性保证,事务之间完全串行执行。


05
一张图看清关系

图片

06
MVCC 管不了写

MVCC 只解决了读的问题(快照读),写的时候依然需要锁。

InnoDB 里有两种读:

快照读(Snapshot Read):普通 SELECT,走 MVCC,不加锁。

当前读(Current Read):SELECT ... FOR UPDATE、UPDATE、DELETE、 INSERT。这些必须读到当前最新版本(不然 UPDATE 可能基于过时数据修改),所以不走 MVCC,走行锁。

InnoDB 在 RC 和 RR 级别下的实际策略是:

读用 MVCC → 无锁快照读,不阻塞写

写用行锁 → 互斥,保证数据安全

读写之间不冲突(读的是历史版本,写的是当前版本),写写之间通过行锁互斥。这才是 InnoDB 并发性能好的真正原因,不是单靠 MVCC 或者单靠锁,而是两者的分工配合。


07
说在最后

MVCC 是一个底层的并发控制工具,解决的是读操作如何不加锁地获取一致数据这个问题。

但一致到什么程度:是只看已提交的最新数据(RC),还是整个事务期间数据不变(RR),还是连未提交的都能看(RU),还是读也加锁保证绝对串行(Serializable),这是业务层面的选择,由隔离级别来控制。

MVCC 提供能力,隔离级别定义策略,它们不是同一层面的东西

就这点事而已!

1、VC++26 静态反射:从 ^^ 开始

2、Vue3 Office 预览神器杀疯了!最难搞的 doc / xls 终于支持!

3、Vue Native 新阶段!以后不再依赖 RN?

4、模型新秀 Jev 是什么,有什么用?

5、同一块内存,六种读法,只有三种合法:你的严格别名判据

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/201521