MySQL RR 级别的实现
RR 级别的实现
如何同时避免不可重复读问题和幻读问题的呢?
MySQL 让多个事务并发运行的时候能互相隔离,避免同时读写一条数据的时候有影响,是借助 undo log 版本链条和 ReadView 机制。
RR 级别下,你这事务读一条数据,无论读多少次,都是一个值:
别的事务修改数据了后即使提交了,你也看不到人家修改的值,这就解决了不可重复读
其它事务插入一些新数据,你也读不到,这就避免幻读
假设有一条数据是事务 id=50 的一个事务插入的,此时有事务 A、B 同时在运行
这时,事务 A 发起了一个查询,第一次查询就会生成一个 ReadView:
creator_trx_id=60
min_trx_id=60
max_trx_id=71
m_ids=[60, 70]
这时,事务 A 基于该 ReadView 去查这条数据,发现这条数据的 trx_id 为 50,是小于 ReadView
里的 min_trx_id 的,说明他发起查询之前,早就有事务插入这条数据还提交了,所以此时可以查到这条原始值的
接着事务 B 此时更新了这条数据的值为 b,修改 trx_id=70,同时生成一个 undo log,事务 B 此时提交
ReadView 中的 m_ids 此时还是 60、70,因为 ReadView 一旦生成了就不会改变!
这时虽然事务 B 已提交,但事务 A 的 ReadView 里, 还是有 60、70,即在你事务 A 开启查询时,事务 B 当时是在运行的意思而已。
然后事务 A 查询这条数据,发现此时数据的 trx_id=70,在 ReadView 的 min_trx_id 和 max_trx_id 的范围,还在 m_ids 列表,这说明啥?
事务 A 开启查询时,id=70 的这个事务 B 还在运行,然后由这个事务 B 更新了这条数据,所以此时事务 A 不能查询到事务 B 更新的这个值,因此顺着指针往历史版本链条上去找,找到 trx_id=50,是小于 ReadView 的 min_trx_id 的,说明在他开启查询之前,就已提交该事务,所以事务 A 可查询到该值,此时事务 A 查到的就是原始值。这就解决了不可重复读。
事务 A 多次读同一个数据,每次读到的都是一样的值,除非是他自己修改的值,否则读到的一直一样。不管别的事务如何修改数据,事务 A 的 ReadView 始终不变,他基于这 ReadView 看到的值始终如一!
如何解决幻读
假设事务 A 先
此时可能查到的就是一条数据,而且读到的是这条数据的原始值的那个版本:
现在,有个事务 C 插入一条数据,然后提交:
接着,事务 A 再查询,发现符合条件的有 2 条数据:
原始值那个数据
事务 C 插入的那条数据
但 C 插入的那条数据的 trx_id=80 > ReadView 的 max_trx_id,说明是自己发起查询后,这个事务才启动,所以此时这条数据不能查询。
因此事务 A 本次查询,还是只能查到原始值那一条数据。所以这里事务 A 不会发生幻读,他根据条件范围查询的时候,每次读到的数据都是一样的,不会读到人家插入进去的数据,这都是依托 ReadView 机制实现的。
版权声明: 本文为 InfoQ 作者【JavaEdge】的原创文章。
原文链接:【http://xie.infoq.cn/article/171908b99f289c47375d39c1d】。文章转载请联系作者。
评论