一、实现原理

MySQL默认的隔离级别是可重复读,即:事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。
那么MySQL可重复读是如何实现的呢?使用的的一种叫MVCC的控制方式 ,即多版本并发控制(Mutil-Version Concurrency Control),类似于乐观锁的一种实现方式。

MVCC实现:InnoDB存储引擎默认在每行记录后面保存两个隐藏的列,用来保存记录的创建时间和删除时间。这里存储的并不是实际的时间值,而是系统版本号。当数据被修改时,版本号加1
在读取事务开始时,系统会给当前读事务一个版本号,事务会读取版本号 <= 当前版本号的数据
此时,如果其他写事务修改了这条数据,那么这条数据的版本号就会加1,从而比当前读事务的版本号高,读事务自然就读不到更新后的数据了。

InnoDB会根据以下两个条件检查每行记录:
InnoDB只会查找版本早于当前事务版本的数据行(也就是行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
行的删除版本要么未定义,要么大于当前事务版本号(这可以确保事务读取到的行,在事务开始之前未被删除),只有条件1、2同时满足的记录,才能返回作为查询结果.

二、实例

假设初始版本号为1:

INSERT
insert into user (id, name) values (1,'Tom');
id   name   create_version delete_version
1   Tom      1       undefined

下面模拟一下文章开头的场景:

SELECT (事务A)
select * from user where id = 1;
此时读到的版本号为1

UPDATE(事务B)
update user set name = 'Jerry' where id = 1;
在更新操作的时候,该事务的版本号在原来的基础上加1,所以版本号为2。
先将要更新的这条数据标记为已删除,并且删除的版本号是当前事务的版本号,然后插入一行新的记录

id   name   create_version   delete_version
1   Tom       1            2 
1   Jerry      2           undefined

SELECT (事务A)
此时事务A再重新读数据:

select * from user where id = 1;
由于事务A一直没提交,所以此时读到的版本号还是为1,所以读到的还是Tom这条数据,也就是可重复读

DELETE
delete from user where id = 1;
在删除操作的时候,该事务的版本号在原来的基础上加1,所以版本号为3
删除时,将当前版本号作为删除版本号

id   name   create_version   delete_version
1    Jerry        2            3