问题描述
在最近的几个小时中,我研究了有关不同SQL事务隔离级别的文档,发现MySQL默认使用重复性读隔离,并进行了一些实验。
据我了解,正在进行的事务中的选择应该看到相同的数据,除非同一事务对其进行更新。
我在使用原子增量(例如update table set age=age+1 where id=1
)时发现了不可重复的读取。
我的测试表由两列id
和age
组成,其中一个条目为1,20
。
在2个会话中运行以下命令,我得到了不可重复的读取:
Transaction 1 Transaction 2
--------------- -------------------
begin; begin;
select * from test; select * from test;
+----+-----+ +----+-----+
| id | age | | id | age |
+----+-----+ +----+-----+
| 1 | 20 | | 1 | 20 |
+----+-----+ +----+-----+
update test set \
age=age+1 where id=1;
select * from test; select * from test;
+----+-----+ +----+-----+
| id | age | | id | age |
+----+-----+ +----+-----+
| 1 | 21 | | 1 | 20 |
+----+-----+ +----+-----+
commit;
select * from test;
-- age = 20
update test set age=age+1 where id=1;
select * from test;
-- Expected age=21
-- got age=22 => Non-Repeatable Read
为什么更新使用的值与选择返回的值不同?想象一下,在行更新之后,我将进行选择并将返回值增加一个。我会得到不同的结果。
解决方法
从右列的连接进行的UPDATE操作将阻塞,直到左侧的事务完成。如果要在两个连接上重复读取,则需要在两个连接上使用BEGIN / COMMIT。
,运行此类代码的正确方法是在第一个FOR UPDATE
的末尾使用SELECT
。如果没有这些,您将在寻找自己发现的麻烦。
我认为发生的事情是(在右侧):
- 右边的第二个SELECT进行了“可重复读取”,只有20个。
- UPDATE发现已提交了21个,因此将其提高到22个。
- 您更改了行的第三个SELECT新内容,因此它重新读取了该行,得到22。