mysql默认repeatable read通过mvcc+next-key locks实现幻读防护,read committed每次select新建read view导致不可重复读,serializable因隐式锁致高并发性能骤降。

MySQL 默认用的是 REPEATABLE READ,但这个级别在标准 SQL 里并不能完全防幻读,InnoDB 是靠 MVCC + Next-Key Locks 补上的——这点不搞清楚,查出来的数据“凭空多了一条”或“两次查询结果不一致”,就容易误判是 bug。
为什么 READ COMMITTED 还会看到不可重复读?
因为每次 SELECT 都会新建一个一致性视图(Read View),只要别的事务在这次查询前提交了修改,当前查询就能看到新值。比如事务 A 第一次查 balance = 100,事务 B 提交更新为 90,A 再查一次就变成 90——不是缓存问题,是隔离级别本身允许的。
-
READ COMMITTED下,UPDATE/DELETE语句只锁住实际命中的行(行锁),不锁间隙 - 同一事务内多次
SELECT可能返回不同结果,但绝不会读到未提交的数据(防脏读) - 在高并发统计类场景(如实时订单数)中,这个级别比
REPEATABLE READ更轻量,锁冲突少
REPEATABLE READ 真的能防幻读吗?
标准定义里不能,但 InnoDB 实现上基本可以——前提是用的是普通 SELECT(快照读)。它依赖事务启动时生成的全局 Read View,后续所有快照读都基于这个版本,所以看不到其他事务插入/删除的新记录。
- 但如果你用了
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE(当前读),InnoDB 就会加Next-Key Lock(行锁 + 间隙锁),把插入点也锁住 - 如果业务里混用快照读和当前读,比如先查再加锁更新,就可能出现“幻读感知”:快照读没看到新行,当前读却锁到了新插入的行
- 注意:
REPEATABLE READ不等于“数据永不变化”,只是对你这个事务而言,在它生命周期内看到的数据版本固定
SERIALIZABLE 为什么几乎没人用?
它会让所有普通 SELECT 隐式加上 LOCK IN SHARE MODE,相当于给整张表或扫描范围加共享锁。看似安全,实则代价巨大。
- 并发写入极易被阻塞,哪怕只是读操作也会等写锁释放
- 在 OLTP 场景下,QPS 可能直接掉一个数量级
- 真正需要串行化语义的逻辑(比如库存扣减+生成订单),应该靠应用层加分布式锁或数据库唯一约束+重试,而不是靠隔离级别硬扛
最容易被忽略的一点:隔离级别控制的是「读可见性」,不是「写互斥性」。UPDATE 语句无论在哪种级别下,都会对目标行加行锁;而是否锁间隙、是否复用快照、是否允许其他事务插入,才由隔离级别和语句类型共同决定。别指望调个 SET TRANSACTION ISOLATION LEVEL 就能解决所有并发问题。











