MySQL 锁 ——No.4 当前读、快照读、快照读实现原理及RC|RR 隔离级别下的 InnoDB 的非阻塞读如何实现

当前读

诸如select ... lock in share modeselect ... for updateupdatedeleteinsert均为当前读;当前读本质上是加了锁的增删该查语句,无论上的是共享锁还是排他锁均为当前读.
这些语句被称为当前读的根本原因是因为它读取的是记录的最新版本,并且在读取之后,还需保证其他事务不能修改当前记录,对读取的记录加锁;上面的除 select 语句加的是共享锁外,其他的都是排他锁,那为什么 updatedeleteinsert也被称为当前读呢?
我们都知道 RSBMS 主要由两大部分组成,一部分是程序实例(即 MySQL 实例),另一部分是存储(即 InnoDB),
我们在执行 update语句更新某几行数据时,每次都需要先读取相应数据行,然后在更新,这个时候在读取的时候需要读取最新行,所以需要使用当前读

快照读

快照读也叫非阻塞读,即所谓快照读就是不加锁的非阻塞读,就是我们最简单的 select操作

当然了,这里不加锁的非阻塞读是以事务隔离级别不为最高级别的前提下成立,因为在最高隔离级别下,快照读也会变成当前读,在其后自动加lock in share mode

快照读的实现原理

之所以出现快照读,是基于提升并发访问性能考虑的;快照读的实现是基于多版本的并发控制,即 MVCC,可以认为 MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁的操作,因此开销更低.
既然基于多版本实现,那么快照读有可能读到的并不是数据的最新版本,可能是之前的历史版本。

在 RC【Read Committed】 隔离级别下,快照读和当前读读到读数据版本是一样的;

演示.

1.开启两个会话,并设置相应的事务隔离级别为 RC

【下面的命令是基于 MySQL 8.0的,8以下的版本可以自行查找相应的命令进行替换】
通过show variables like 'transaction%';查看会话的隔离级别,然后通过set session transaction isolation level read committed;设置会话的隔离级别为 RC

image.png

2.在一个会话中查询数据,另一个会话中修改相应数据

1)会话 1 中查询 deptno = 10 的 loc 为 NEW YORK

image.png

2)会话 2 中修改 deptno = 10 的 loc 为纽约
image.png

3)在会话1中分别使用当前读和快照读读取 deptno = 10 的数据
发现使用快照读和当前读读取到到数据是一样的,即在 RR 隔离级别下,使用快照读读取到到也是最新数据
image.png

而在 RR【Repeatable Read】隔离级别下,当前读返回的是数据的最新版本,而快照读在该隔离级别下可能读到数据的历史版本.在 RR 隔离级别下,事务首次调用快照读的时机很关键,即创造快照的时机决定了快照的版本

演示

1. 第一种情况,会话 1 先快照读读取 deptno = 40 的数据行,会话2修改 deptno = 40的数据行的 loc = 奥地利,并提交会话,然后比较会话 1 通过快照读和当前读读取读数据情况
1)会话 1 先快照读读取 deptno = 40 的数据行

image.png

2)会话2修改 deptno = 40的数据行的 loc = 奥地利,并提交,并查询结果显示已修改成功
image.png

3)比较会话 1 通过快照读和当前读读取读数据情况
结果显示,快照读读取到的是旧数据,而当前读读到读是最新数据
image.png

1. 第二种情况,会话2修改 deptno = 40的数据行的 loc = 乌克兰,并提交会话,然后比较会话 1 通过快照读和当前读读取读数据情况
1)会话2修改 deptno = 40的数据行的 loc = 乌克兰,并提交会话

image.png

2)比较会话 1 通过快照读和当前读读取读数据情况
可以看到当前读和快照读读取到的数据是一致的
image.png

RC|RR 隔离级别下的 InnoDB 的非阻塞读【即快照读】如何实现

快照读的实现依赖三个因素;每行数据记录除了存储数据以外,还有一些额外的字段,其中最关键的是三个:DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID字段

DB_TRX_ID

该字段用来标识最近一次对本行记录做修改,无论是insert,还是update,它都是事务的标识符,即最后一次修改本行记录的事务ID,delete对于innodb来说也是一个update操作,更新行中的一个特殊位,将行标识为deleted,并非做真正的删除【每次开启一个事务的时候,该事务ID就会递增,即越新开启的事务,它的 事务 ID 越大】

DB_ROLL_PTR

回滚指针,只写入回滚段( roll back segment)的undo 日志记录。如果一行记录被更新,则undo log report 包含重建该行记录被更新之前内容所必须的信息。

DB_ROW_ID

即行号包含一个随着新行插入而单调递增的行ID,当innodb自动产生聚集索引时,聚集索引会包括这个行ID的值,否则这个行ID不会出现在任何索引中。 (以前提到的,在innodb存储引擎中,如果表中没有设置主键并且无唯一键时,Innodb会为我们创建一个隐藏主键字段,即我们这里的DB_ROW_ID)
光有上面这三个字段,并不足以实现快照读,还需要依托undo日志。

undo 日志

当我们对记录做了变更操作时,就会产生undo记录,undo记录中存储的是老版数据,当一个旧的事务需要读取数据时,为了能够读取到老版本的数据,需要顺着undo列找到满足其可见性的记录,这个找满足可见行的记录依赖 read view
undo 日志主要分为两种:即insert undo log 和 update undo log.

insert undo log

表示的是事务对insert新记录产生的undo log,只在事务回滚时需要,并且在事务提交后就可以立即丢弃

update undo log

事务对记录进行delete或者update操作时产生的undo log,不仅在事务回滚时需要,快照读也需要,所以不能随便删除,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被线程删除。

read view

read view主要是用来做可见性判断的,即当我们去执行快照读 select 的时候,会针对我们查询的数据创建出一个 read view,来决定当前事务能看到的是哪个版本的数据,有可能是当前最新版本的数据,也可能是 undo log 中某个版本的数据,read view 遵循一个可见性算法。主要是将要修改的数据的 DB_TRX_ID 取出来,与系统其他活跃事务ID【DB_TRX_ID】做对比,如果大于或者等于这些 ID 的话,就通过 DB_ROW_PTR 指针去取出 un do log,上一层的 DB_TRX_ID 直到小于这些活跃事务 ID 为止,这样就保证了我们获取到的数据版本是当前可见的最稳定的版本

总结

正是以上的三个因子才使得 InnoDB 在 RR、RC 隔离级别下支持非阻塞读,而读取数据时的非阻塞就是所谓的 MVCC【Multiversion concurrency control】 ,而 InnDB 非阻塞读机制实现了仿照版的 MVCC,MVCC 代表多版本并发控制,读不加锁,读写不冲突,极大的增加了系统的并发性能,那为什么是伪 MVCC 机制呢,因为并没有实现核心的多版本并存,而undo log 中的内容只是串行化的结果,记录了多个事务的过程,不属于多版本共存。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342