问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

MySQL可重复读隔离级别:真的能完全避免幻读吗?

创作时间:
作者:
@小白创作中心

MySQL可重复读隔离级别:真的能完全避免幻读吗?

引用
CSDN
1.
https://blog.csdn.net/qq_45981373/article/details/139396954

MySQL InnoDB引擎的默认隔离级别是「可重复读」,这个级别在很大程度上避免了幻读现象,但并不是完全解决了。本文将通过两个具体场景来说明这个问题。

前言

在可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的。对于普通select语句(快照读),是通过MVCC(多版本并发控制)方式解决了幻读;而对于select ... for update等语句(当前读),是通过next-key lock(记录锁+间隙锁)方式解决了幻读。但是,在某些特殊情况下,幻读现象仍然可能发生。

第一个发生幻读现象的场景

以一张表t_stu为例:

事务A执行查询id = 5的记录,此时表中没有该记录:

# 事务 A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_stu where id = 5;
Empty set (0.01 sec)

然后事务B插入一条id = 5的记录并提交:

# 事务 B
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t_stu values(5, '小美', 18);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

此时,事务A尝试更新id = 5的记录。虽然事务A看不到这条记录,但更新操作仍然会成功,因为这条记录的trx_id隐藏列的值会被设置为事务A的事务ID。之后,事务A再次查询id = 5的记录时,就能看到这条记录了,这就是幻读现象。

第二个发生幻读现象的场景

在这个场景中:

  • T1时刻:事务A执行快照读语句select * from t_test where id > 100,得到3条记录。
  • T2时刻:事务B插入一条id = 200的记录并提交。
  • T3时刻:事务A执行当前读语句select * from t_test where id > 100 for update,得到4条记录,发生了幻读。

为了避免这类特殊场景下的幻读现象,建议在开启事务后立即执行select ... for update等当前读语句,因为这会为记录添加next-key lock,从而阻止其他事务插入新记录。

总结

MySQL InnoDB引擎的可重复读隔离级别通过以下方式避免幻读:

  • 对于快照读(普通select语句),使用MVCC机制。
  • 对于当前读(select ... for update等语句),使用next-key lock机制。

但是,这两个解决方案并不能完全避免幻读。例如:

  • 当事务A更新了事务B插入的记录时,前后两次查询结果不一致,会发生幻读。
  • 如果事务先执行快照读,然后其他事务插入新记录,后续的当前读会发现记录条目不一致,也会发生幻读。

因此,MySQL的可重复读隔离级别只是在很大程度上避免了幻读,而不是完全解决。要避免这类特殊场景下的幻读,建议在事务开始后立即执行当前读语句。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号