可重复读_MySQL的可重复读到底是怎么实现的?图解
ReadView机制
通过上⼀篇关于事务隔离的⽂章,你已经知道了事务有 4 个隔离级别:读未提交、读已提交、可重复读、串⾏化,随着隔离级别的加强,能解决脏写、脏读、不可重复读、幻读的问题。
undertale heital在之前关于存储引擎的⽂章中,也着重提到了,InnoDB 是 MySQL 默认的存储引擎,InnoDB 默认的隔离级别就是可重复读
数据可视化按照数据类型分类可重复读。在这个隔离级别下,开启事务之后,多次读写同⼀⾏数据,读到的值永远是⼀致。那 MySQL 是如何做到这⼀点的呢?
本⽂就进⼀步聊聊,⼤名⿍⿍的读已提交是如何实现的。
回顾⼀下 undo log 回滚⽇志
在讲可重复读的底层原理之前,我们有必要看⼀下之前画的图,重新回顾⼀下 undo log 回滚⽇志。
当 MySQL 执⾏写操作之前,会把即将被修改的数据记录到 undo log ⽇志⾥⾯。
只有这样,事务要回滚的时候,即使 Buffer Pool 中的数据被修改了,依然可以从 undo log ⽇志中,读取到原插⼊、修改、删除之前的
回滚操作。
值,最终把值重新变回去,这就是回滚
undo log 版本链
undo log 版本链是基于 undo log 实现的。undo log 中主要保存了数据的基本信息,⽐如说⽇志开始的位置、结束的位置,主键的长度、表id,⽇志编号、⽇志类型。
此外,undo log 还包含两个隐藏字段 trx_id 和 roll_pointer。trx_id 表⽰当前这个事务的 id,MySQL 会为每个事务分配⼀个 id,这个id 是递增的。roll_pointer 是⼀个指针,指向这个事务之前的 undo log。
如下图所⽰,现在有⼀个 id 为 10 的事务 A 正在执⾏,undo log ⽇志的信息如下所⽰:
splice安卓版手机客户端紧接着 id 为 18 的事务 B 开始执⾏,就会再⽣成⼀条 undo log ⽇志,同时新⽣成的⽇志的 roll_pointer 指向上⼀条 undo log ⽇志。
⽇志与⽇志之间通过 roll_pointer 指针连接,就形成了 undo log 版本链。
基于 undo log 版本链实现的 ReadView 机制
铺垫了这么多,到这⾥终于可以说说什么是 Readiew 了。
ReadView 说⽩了就是⼀种数据结构,它主要包含这样⼏部分:
m_ids,当前有哪些事务正在执⾏,且还没有提交,这些事务的 id 就会存在这⾥;
min_trx_id,是指 m_ids ⾥最⼩的值;
max_trx_id,是指下⼀个要⽣成的事务 id。下⼀个要⽣成的事务 id 肯定⽐现在所有事务的 id 都⼤;
oracle创建库语句creator_trx_id,每开启⼀个事务都会⽣成⼀个 ReadView,⽽ creator_trx_id 就是这个开启的事务的 id。
为了能把这些概念说清楚,接下来我会画⼏张图帮助⼤家理解,不明⽩的可以多看⼏遍图!
先来看第⼀张图。
事务是可以并发执⾏的,现在有事务 A、事务 B 这两个事务,且这两个都没有提交。事务 A 将会执⾏多次读操作,来模拟可重复读中多次读取同⼀⾏数据的场景。事务 B 则会修改这⼀⾏数据。
事务 A 开启事务的时候会⽣成⼀个 ReadView,所以说这个 ReadView 的创建者就是事务 A,事务 A 的事务 id 是 10,所以
creator_trx_id 就是 10。
此时,总共就只有事务 A、事务 B 这两个事务,⽽且它们都还没有提交,所以说 m_ids 会把这两个事务 id,10、18 都记录下来。
min_trx_id 是 m_ids ⾥⾯的最⼩值,10、18 中最⼩的显然是 10。当前最⼤的事务 id 是 18,那么下⼀个事务的 id 就是
19,max_trx_id 就是 19。
ReadView ⽣成之后,事务 A 就要去 undo log 版本链中读取值了。
现在只有⼀条 undo log ⽇志,但这并不意味着事务 A 就能读到这条⽇志的值 X。它要先判断这⾏⽇志的 trx_id 是否⼩于当前事务的
min_trx_id。看图我们可以很轻松地发现,⽇志的 trx_id = 8 ⼩于 ReadView 中 min_trx_id = 10。
这就意味着,这个事务 A 开始执⾏之前,修改这⾏数据的事务已经提交了,所以事务 A 是可以查到值 X 的。
如何基于 ReadView 实现可重复读?
我们继续看,事务 A 第⼀次读完之后,事务 B 要修改这⾏数据了。undo log 会为所有写操作⽣成⽇志,所以就会⽣成⼀条 undo log ⽇志,并且它的 roll_pointer 会指向上⼀条 undo log ⽇志。
紧接着,事务 A 第⼆次去读这⾏数据了,情况如下图所⽰:
第⼀次读的时候,开启事务 A 的时候就⽣成了⼀个 ReadView,R
此时事务 A 第⼆次去查询的时候,先查到的是 trx_id = 18 的那条数据,它会发现 18 ⽐最⼩的事务编号 10 ⼤。那就说明事务编号为 18的事务,有可能它是读不到的。
接着就要去 m_ids ⾥确认是否有 18 这条数据了。发现有 18,那就说明在事务 A 开启事务的时候,这个事务是没有提交的,它修改的数据就不应该被读到。
事务 A 就会顺着 roll_pointer 指针继续往下,到了 trx_id = 8 这条⽇志,发现这条能读,读到的值任然是 x,与第⼀次读到的结果⼀致。
成功实现可重复读!
思考题
mysql怎么读英语开发者工具网络连接失败显示不了二维码这篇⽂章在事务隔离机制的基础上,进⼀步介绍了 MySQL 中的 undo log 版本链、ReadView,并且⽤画图的⽅式知道了 MySQL 中可重复读这⼀个隔离级别是如何实现的。
最后,留下⼀个思考题,如果事务A再次修改了这⾏数据,那么事务A能读到么?
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论