可重复读隔离级别实例分析

一叶而不知秋

向管中窥豹寻知外,坐井观天又出来。

背景条件

autocommit=1;隔离级别为repeatable read。

上表中 trx_1,trx_2,trx_3 代表三个客户端启动的三个事务,语句从上到下依次执行,上面三个查询语句的结果分别是多少。

先说下答案:(1)=>3;(2)=>1;(3)=>2。接下来我们分析一下为什么会得到上面的结果。

MVCC

MySQL隔离机制通过MVCC实现。简单来说就是:MySQL事务启动时会生成一个机遇整库的“快照”(read-view),事务中的读都是“快照”读。

快照并非真实存在,而是根据undo log计算出的

数据库中的每行记录后面有三个隐藏列,其中一个记录事务当前id的trx_id(并不是启动快照的事务id,而是最新更新该行并且提交的事务id)。如下图:

对表中某一列进行了三次更新,得到四个read-view,中间的U1,U2,U3代表undo log;第一个事务,id为15,提交后将k的值改为10,行上隐藏的trx_id更改为15。后面类似。

根据可重复读的定义,可以得到:当前事务只承认在自己事务启动前已经提价的事务修改的数据,也就是说trx_id小于当前事务id,并且在该事务启动前已经提交。

查询

结合本例来看,假设trx_1事务开启前系统只有一个活跃事务Id:99;该行的trx_id = 90;trx_1,trx_2,trx_3启动后的事务id分别为100,101,102。

经过两次更新后,该行的状态如下图:

trx_1开始读的时候,从当前版本开始读也就是trx_2更新后的版本开始读。但是trx_2事务未提交并且trx_id=101 > 100,因此对于trx_1不可见;根据undo log找上一个版本也就是trx_3更新的版本,事务已经提交,但是trx_id=102 > 100,对于trx_1不可见;继续找上一个版本,也就是trx_id = 90的版本,已经提交,且trx_id小于100,于是trx_1返回这个版本的值,也就是1。

更新

更新的流程是先读后写,但是考虑上面的分析,trx_2的update后select的值好像不正确。根据可重复读的定义,应该是2,但是返回了3。

MySQL中,更新语句的读是当前读。也就是update语句读到的是最新一次更新的值,不管更新该行的事务有没有提交。除了update语句,加锁读也是当前读,即select语句后加上lock in share mode或者for update,读到的都是当前值,而不是“快照”中的值。

总结

可重复读的核心是一致性读。

更新数据,或者加锁读是当前读。

当记录的行锁被其他事务占用的话,需要进入锁等待。

可重复读隔离级别下,当前事务启动前提交的事务可见,其余不可见。

读提交隔离级别下,当前查询语句前提交的事务可见,其余不可见。

你如果想学技术 | 屯干货 | 聊职场

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181214G18ATU00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券