MySQL REPEATABLE-READ隔离级别实战:利用受影响的行数判断更新是否生效

Mason
Mason
发布时间:2026-10-14 | 5人浏览过
原创

在MySQL的REPEATABLE-READ(可重复读)隔离级别下,事务内的SELECT操作具有快照一致性,不会读到其他事务已提交的修改。但UPDATE操作的执行结果却依赖于当前锁的状态和行版本号。本文通过多会话并发操作实例,解析如何利用UPDATE语句返回的“Affected rows”(受影响行数)是否为0,来判断目标记录的状态是否已被其他事务抢先修改,这是一种在高并发场景下判断更新是否成功的有效策略。

结果或目标

理解MySQL REPEATABLE-READ 隔离级别下数据可见性与行更新的机制,掌握通过Affected rows返回值判断业务状态变更的方法。

准备条件

  • MySQL数据库环境(默认隔离级别即为REPEATABLE-READ)。
  • 测试表结构,例如:CREATE TABLE test.dd (id INT PRIMARY KEY, aa INT);。
  • 两个独立的数据库客户端连接,分别代表“会话1”和“会话2”。

真实操作顺序

第1步:开启会话1的事务并禁用自动提交

目的:创建一个稳定的查询视图,确保后续SELECT读取的是事务开始时的快照数据。

入口:客户端1(会话1)。

动作:设置自动提交关闭,防止中间操作立即写入磁盘,保持事务状态。

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

第2步:会话1读取基准数据

目的:获取初始状态数据,作为后续对比的基准。

入口:客户端1(会话1)。

动作:执行SELECT查询。

mysql> select * from test.dd where id=1;
+----+------+
| id | aa |
+----+------+
| 1 | 2 |
+----+------+
1 row in set (0.00 sec)

完成状态:确认当前 aa 字段的值为 2。

第3步:会话2修改数据并提交

目的:模拟另一个事务对同一行数据进行更新并永久化该更改。

入口:客户端2(会话2)。

动作:将 id=1 的记录中 aa 字段从 2 更新为 1,并提交事务。

-- 会话2
mysql> update test.dd set aa=1 where id=1;
Query OK, 1 row affected (0.09 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> commit;

第4步:会话1验证快照一致性

目的:证明在REPEATABLE-READ隔离级别下,未重启事务前,SELECT依然看到旧数据。

入口:客户端1(会话1)。

动作:再次执行相同的SELECT查询。

mysql> select * from test.dd where id=1;
+----+------+
| id | aa |
+----+------+
| 1 | 2 |
+----+------+
1 row in set (0.00 sec)

观察:尽管会话2已经提交了 aa=1 的更改,但会话1依然读到 aa=2。这是MVCC(多版本并发控制)提供的快照读特性。

第5步:会话1尝试更新并观察受影响行数

目的:核心步骤。演示当基于旧快照数据执行UPDATE时,如果底层数据已被他人修改,会发生什么。

入口:客户端1(会话1)。

参数:尝试将 aa 从 2 更新为 1(与会话2最终值一致,但原始值是2)。

mysql> update test.dd set aa=1 where id=1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0

关键结果:显示 0 rows affected。

原理分析与完成判断

为什么SELECT读到的是 2,UPDATE却显示影响了0行?

  1. 快照读 vs 当前读:SELECT是快照读,看到的是事务开始时的版本;而UPDATE是当前读,它必须锁定并查看最新的数据版本以确定是否真的需要改变值。
  2. 更新逻辑:InnoDB在执行 UPDATE test.dd SET aa=1 WHERE id=1 时,会检查当前数据库文件中 aa 的实际值。由于会话2已经将其改为 1,而UPDATE语句的目标值也是 1,即“新值等于旧值”,因此InnoDB判定无需进行物理层面的数据页修改。
  3. 受影响行数(Changed Rows):只有在数据库引擎真正修改了行的内容时,“Changed”才会增加。若新旧值相同,虽然匹配了WHERE条件(Matched=1),但实际未发生变更(Changed=0)。

简要总结

在开发涉及状态位(如订单状态、库存扣减校验)的逻辑时,直接相信SELECT的结果可能存在风险(因为SELECT可能读到过期快照)。更稳健的做法是利用REPEATABLE-READ特性,执行UPDATE操作,并严格检查其返回的Affected rows:

  • 若为 1:说明该行曾被修改且确实发生了数值变化,或者此前从未存在被正确更新。
  • 若为 0:结合业务逻辑,通常意味着该行数据已被其他事务抢先修改到了目标值,或者当前事务持有的数据版本与数据库现实不符导致无需/不能更新。

以上方法在处理高并发竞争条件时,是一种简洁且有效的乐观锁实现思路。

以上就是MySQL REPEATABLE-READ隔离级别下利用UPDATE受影响行数判断更新状态的详细内容,更多关于MySQL事务控制与并发优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表