在 MySQL 中,不可重复读(Non-repeatable Read)是指在同一个事务内,两次读取同一行数据时得到的结果不一致。这种情况通常发生在使用较低的隔离级别(如 READ COMMITTED)时,其他事务可能在事务执行期间提交了新的数据,导致当前事务多次读取同一行数据时得到不同的结果。
具体场景举例:
假设有两个事务:Transaction A 和 Transaction B,它们同时操作数据库表中的数据。
-
事务 A 执行过程:
- 事务 A 开始后读取了某一行数据(假设字段值为 X)。
- 在事务 A 读取数据后,但在事务 A 提交之前,事务 B 修改了同一行数据的字段值(假设变为 Y)并提交了事务。
-
事务 A 的不可重复读问题:
- 在事务 A 提交前,再次读取同一行数据时,得到的结果是 Y,而不是之前读取的 X。
- 这样,事务 A 在同一个事务内多次读取同一行数据时,得到的结果不一致,即出现了不可重复读的问题。
示例 SQL:
考虑以下示例,假设表 users 存在,并且在 READ COMMITTED 隔离级别下进行操作:
-- 开启事务 A
START TRANSACTION;
-- 事务 A 读取某行数据
SELECT * FROM users WHERE id = 1; -- 假设结果为 (id = 1, name = 'John', ...)
-- 同时,在另一个事务 B 中修改了 id = 1 的行
-- 事务 B 执行:
START TRANSACTION;
UPDATE users SET name = 'Jane' WHERE id = 1;
COMMIT;
-- 事务 A 继续执行,在读取相同行时得到的结果可能已经改变
SELECT * FROM users WHERE id = 1; -- 现在结果为 (id = 1, name = 'Jane', ...)
在上述例子中,事务 A 在读取 id = 1 的用户信息时,得到了初始的名字为 'John' 的结果。但是,在事务 A 继续执行之前,事务 B 修改了相同行的名字为 'Jane' 并提交了事务。因此,当事务 A 再次读取 id = 1 的用户信息时,得到的结果变成了 'Jane',而不是之前的 'John',导致了不可重复读的问题。
这种情况可以通过提升事务隔离级别至 REPEATABLE READ 或 SERIALIZABLE 来避免。