还是以表 mvcc_test 为例,假设现在表 mvcc_test 中只有一条由事务 id 为 50 的事务插入的一条记录 , 接下来看一下 READ COMMITTED 和 REPEATABLE READ 所谓的生成 ReadView 的时机不同到底不同在哪里 。
READ COMMITTED:每次读取数据前都生成一个 ReadView;
比方说现在系统里有两个事务 id 分别为 70、90 的事务在执行:
-- T 70
UPDATEmvcc_test SETname='habit_trx_id_70_01'WHEREid=1;
UPDATEmvcc_test SETname='habit_trx_id_70_02'WHEREid=1;
此时表 mvcc_test 中 id 为 1 的记录得到的版本链表如下所示:

文章插图
假设现在有一个使用 READ COMMITTED 隔离级别的事务开始执行:
-- 使用 READ COMMITTED 隔离级别的事务
BEGIN;
-- SELECE1:Transaction 70、90 未提交
SELECT*FROMmvcc_test WHEREid=1;
-- 得到的列 name 的值为'habit'
这个 SELECE1 的执行过程如下:
在执行 SELECT 语句时会先生成一个 ReadView,ReadView 的 m_ids 列表的内容就是 [70, 90],min_trx_id 为 70,max_trx_id 为 91,creator_trx_id 为 0 。
然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 habit_trx_id_70_02,该版本的 trx_id 值为 70,在 m_ids 列表内,所以不符合可见性要求第 4 条:如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id之间 min_trx_id < trx_id < max_trx_id,那就需要判断一下trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在 , 说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问 。根据 roll_pointer 跳到下一个版本 。
下一个版本的列 name 的内容是 habit_trx_id_70_01,该版本的 trx_id 值也为 70,也在 m_ids 列表内,所以也不符合要求,继续跳到下一个版本 。
下一个版本的列 name 的内容是 habit , 该版本的 trx_id 值为 50,小于 ReadView 中的 min_trx_id 值,所以这个版本是符合要求的第 2 条:如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问 。最后返回的版本就是这条列 name 为 habit 的记录 。
之后,把事务 id 为 70 的事务提交一下,然后再到事务 id 为 90 的事务中更新一下表 mvcc_test 中 id 为 1 的记录:
【MYSQL事务的底层原理】-- T 90
UPDATEmvcc_test SETname='habit_trx_id_90_01'WHEREid=1;
UPDATEmvcc_test SETname='habit_trx_id_90_02'WHEREid=1;
此时表 mvcc 中 id 为 1 的记录的版本链就长这样:

文章插图
然后再到刚才使用 READ COMMITTED 隔离级别的事务中继续查找这个 id 为 1 的记录,如下:
-- 使用 READ COMMITTED 隔离级别的事务
BEGIN;
-- SELECE1:Transaction 70、90 均未提交
SELECT*FROMmvcc_test WHEREid=1;-- 得到的列 name 的值为'habit'
-- SELECE2:Transaction 70 提交,Transaction 90 未提交
SELECT*FROMmvcc_test WHEREid=1;-- 得到的列 name 的值为'habit_trx_id_70_02'
这个 SELECE2 的执行过程如下:
在执行 SELECT 语句时又会单独生成一个 ReadView,该 ReadView 的 m_ids 列表的内容就是 [90],min_trx_id 为 90,max_trx_id 为 91,creator_trx_id 为 0 。
然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是 habit_trx_id_90_02,该版本的 trx_id 值为 90,在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本 。
下一个版本的列 name 的内容是 habit_trx_id_90_01,该版本的 trx_id 值为 90,也在 m_ids 列表内,所以也不符合要求,继续跳到下一个版本 。
下一个版本的列 name 的内容是 habit_trx_id_70_02,该版本的 trx_id 值为 70,小于 ReadView 中的 min_trx_id 值 90,所以这个版本是符合要求的,最后返回这个版本中列 name 为 habit_trx_id_70_02 的记录 。
推荐阅读
- 深入探讨数据库管理系统中的不同隔离级别
- Git新手如何上传项目代码到GitHub并完成后续的代码更新?
- 大厂都是怎么做Redis重试的?
- MySQL中的去重技巧和策略
- 边缘计算网关?在生产制造中的应用
- 五个提升SQL语句性能的小窍门
- Python字典的选择之道:掌握六种类型的终极指南!
- 《我的前半生》:陈俊生年薪才150万,为何罗子君敢买8万的鞋子?
- 皂角米保质期一般多久 皂角米的保质期是多久
- 夏季解暑降温的好方法有什么 夏季解暑方法
