MySQL隔离级别的可重复读取和更新中的原子增量

问题描述

在最近的几个小时中,我研究了有关不同SQL事务隔离级别的文档,发现MySQL默认使用重复性读隔离,并进行了一些实验。 据我了解,正在进行的事务中的选择应该看到相同的数据,除非同一事务对其进行更新。 我在使用原子增量(例如update table set age=age+1 where id=1)时发现了不可重复的读取。

我的测试表由两列idage组成,其中一个条目为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。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...