请选择 进入手机版 | 继续访问电脑版
查看: 63|回复: 0

[资料分享] 基于repeatable read使用解析

[复制链接]

签到天数: 193 天

[LV.7]化身百千

发表于 2019-3-14 20:59:11 | 显示全部楼层 |阅读模式

同样是和上面一样的废话,对于熟悉SQL Server的人来说,在说明这个隔离级别之前,必须先给个提醒:MariaDB/MySQL中的重复读和SQL Server中的重复读完全不一样,MariaDB/MySQL中该级别基本类似于SQL Server中快照隔离级别。

在SQL Server中,重复读的查询会申请共享锁,并且在查询结束的一刻不释放共享锁,而是持有到事务结束。所以会造成比较严重的读写并发问题。SQL Server中快照隔离级别实现的是事务级的事务一致性,每次事务开启的时候获取最新的已提交行版本,只要事务不结束,读取的记录将一直是该行版本中的数据,不管其他事务是否已经提交过对应的数据了。但是SQL Server中的快照隔离会有更新冲突:当检测到两边都想要更新同一记录时,会检测出更新冲突,这样会提前结束事务(进行的是回滚操作)而不用再显式地commit或者rollback。

也就是说,MariaDB/MySQL中repeatable read级别总是会在事务开启的时候读取最新提交的行版本,并将该行版本一直持有到事务结束。但是MySQL中的repeatable read级别下不会像SQL Server一样出现更新冲突的问题。

前文说过read committed隔离级别下,读取数据时总是会去获取最新已提交的行版本。这是这两个隔离级别在"一致性非锁定读"上的区别。

另外,MariaDB/MySQL中的repeatable read的加锁方式是next-key lock算法,它会进行范围锁定。这就避免了幻影读的问题(官方手册上说无法避免)。在标准SQL中定义的隔离级别中,需要达到serializable级别才能避免幻影读问题,也就是说MariaDB/MySQL中的repeatable read隔离级别已经达到了其他数据库产品(如SQL Server)的serializable级别,而且SQL Server中的serializable加范围锁时,在有索引的时候式锁范围比较不可控(你不知道范围锁锁住哪些具体的范围),而在MySQL中是可以判断锁定范围的(见innodb锁算法)。

这里要演示的就是在该级别下,读取的行版本数据是不随提交而改变的。

当前示例表ttt的记录如下:

mysql> select * from ttt;
+------+
| id   |
+------+
|    1 |
|    2 |
+------+
在会话1执行:

begin;update ttt set id=100 where id=1
在会话2中执行:

set @@session.tx_isolation='repeatable-read';
begin;select * from ttt;
+------+
| id   |
+------+
|    1 |
|    2 |
+------+
查询的结果和预期的一样,来自开启事务前最新提交的行版本数据。

回到会话1提交事务:

commit;
再回到会话2中查询:

select * from ttt;
+------+
| id   |
+------+
|    1 |
|    2 |
+------+
再次去会话1更新该记录:

begin;update ttt set id=1000 where id=100;commit;
再回到会话2执行查询:

select * from ttt;
+------+
| id   |
+------+
|    1 |
|    2 |
+------+
发现结果根本就不会改变,因为会话2开启事务时获取的行版本的id=1,所以之后读取的一直都是id=1所在的行版本。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /2 下一条

返回顶部