可重复读实现原理大揭秘
- 版本变化规则:
新事务执行系统版本号加一,也就是当前事务版本号;
删除语句:执行后将新的系统版本号作为对应行数据的删除标记;
更新语句:复制原来的数据作为新的一行,将新的版本号给数据行版本,并且将新的版本号作为原来数据行的删除标记; - 何为新事务:
begin之下,commit之上为同一事务,整个作为一个事务,要么全做,要么全不做。在其他客户端,也就是会话的begin之下,commit之上相对上面这个又是一个新事务。 - SELECT
Innodb检查每行数据,确保他们符合两个标准:
1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行
2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号,确定了当前事务开始之前,行没有被删除
符合了以上两点则返回查询结果。
-
实例:
假设系统版本开始为0,A客户端开始一个事务,执行一条插入语句,系统版本加一变为一,将系统版本作为插入生成的数据行的创建版本,也是数据行版本。
删除:
A客户端又开启一个事务,有两条同样的查询语句,均是查询刚刚插入的那条数据。A客户端执行查询语句,系统版本加一由原来的一变为二,当前事务版本为二。注意,重点来了,此刻在A客户端执行第二条查询语句之前B客户端开启了新事务,执行删除语句删除那天插入的数据,系统版本号由二变为三并将系统版本号作为当时插入的数据行的删除标记。最后A客户端执行了第二天同样的查询语句,由于还是同一个事务里面的,系统版本号和第一次执行查询语句时一样,为二,此时根据查询规则:数据行版本小与或等于当前事务,数据行版本为一,当前事务版本为二,符合;数据删除标记为空或者大于当前事务版本,数据删除标记为三,大于当前事务版本号,符合,故最后能够读到原先插入的数据而不是被删除了什么都没看到。
更新:
同样在A客户端执行第一个查询语句后,B客户端执行更新语句更新该条数据,首先系统版本号为三,因为更新语句是删除+复制更新插入,原先的数据的删除标记变为三,复制更新插入的那条数据的创建版本为三,也就是新的数据行的行版本号为三,此时,A客户端执行第二天查询语句,由于不是新事务,跟第一执行的查询是同一个事务,仍然保持原来的当前事务版本为二:此时根据查询规则:数据行版本小与或等于当前事务,数据行版本为一和三,当前事务版本为二,原先数据行符合;数据删除标记为空或者大于当前事务版本,数据删除标记为三,大于当前事务版本号,符合,故最后能够读到原先插入的数据而不是被更新后的数据。 -
总结:
不可重复读是删除和更新造成的,实例很好地证明了可重复读的实现能够解决事务并发带来的不可重复读问题。