REPEATABLE READ如何确定数据行的版本?

REPEATABLE READ如何确定数据行的版本?

众所周知,在事务隔离级别REPEATABLE READ的情况下,我们读到的行数据版本是事务开始时候的版本,但是在具体实现上,这里有一个疑问,数据库到底使用的是begin开始时候的版本?还是begin开始后,我们第一次读相关行数据时候的版本呢?也就是什么时候决定本次事务能够读到的行的版本。先不要急着下结论,让我们以mysqld 8.0.20-debug为例子,看下MySQL究竟是怎么实现的。

1 表

表的schema为:

create table t(
    a int PRIMARY KEY,
    b int
);

insert into t values(1,1),(2,2),(4,4),(5,5);

2 第一个例子

两个session的操作顺序如下:

时间线 session A session B
1 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
begin;
2 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
begin;
update t set b = b * 10 where a = 2;
commit;
3 select * from t where a = 2;

在sessionA中,读到的结果如下所示:

+---+------+
| a | b    |
+---+------+
| 2 |   20 |
+---+------+

可以看到,似乎MySQL是在第一次读该行时决定本次事务应该使用哪个版本?但到底是不是这样呢?让我们把数据恢复到一开始的值,看第二个例子。

3 第二个例子

两个session的操作顺序如下:

时间线 session A session B
1 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
begin;
select * from t where a = 4;//这里读取一个不相关的行
2 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
begin;
update t set b = b * 10 where a = 2;
commit;
3 select * from t where a = 2;

在这个例子里面,不同的地方在表格中已经加黑,而在这个例子里面,sessionA中,读到的结果如下所示:

+---+------+
| a | b    |
+---+------+
| 2 |    2 |
+---+------+

而这一次,因为我们读到了一个不相关的值,却导致了a=2这一行读到的值发生了变化,那么结果其实很显而易见了。

MySQL在决定REPEATABLE READ使用的行版本的时候,其实是在用户第一次读相关行的时候决定的,只不过,这个是以page为单位的,这也就是第二个例子里面,为啥读了a=4的值却导致了a=2的值发生了变化的原因。

这是一个小小的区别,可以帮助我们更好的理解MySQL的运行机制。

原文地址:https://www.cnblogs.com/seancheer/p/15186233.html