rc降低死锁概率的根本原因是默认不使用间隙锁,仅对命中行加记录锁,锁范围更小、冲突更少;而rr对范围条件自动加next-key锁,易引发循环等待死锁。

RC 隔离级别为什么能降低死锁概率
根本原因在于它不默认使用间隙锁(Gap Lock)——除了唯一键/外键冲突检查这类极少数场景。RR 级别下,UPDATE WHERE age > 25 这类范围条件会自动加 Next-Key Lock(Record + Gap),锁住整个区间;而 RC 下只锁实际命中的行,锁范围更小、冲突面更窄。
常见死锁链:事务 A 锁了 (10, 20) 间隙,事务 B 锁了 (20, 30),然后都试图插入 25 → 直接卡死。RC 下这个间隙不被锁,插入意向锁(Insert Intention Lock)之间天然兼容,基本不会触发这类循环等待。
无序列表说明关键差异:
- RR 下
SELECT ... FOR UPDATE或UPDATE带范围条件时,InnoDB 自动升级为 Next-Key Lock - RC 下同一条 SQL 只加 Record Lock,除非涉及唯一索引冲突(此时仍会加 S 型 Next-Key Lock 做重复校验)
- RC 减少 MVCC 版本链维护开销,尤其在写多读少场景下,undo log 清理压力更小
- 间隙锁缺失 ≠ 完全没锁:外键约束、唯一键冲突检测仍会触发 Gap Lock,这点常被忽略
RC 下 INSERT 死锁的真实诱因
很多人误以为 RC 就完全没 INSERT 死锁,其实不然。核心陷阱在唯一键冲突和主键冲突的锁行为上——RC 和 RR 在这些场景下锁策略一致。
比如两个事务并发执行:
INSERT INTO users (id, email) VALUES (100, 'a@b.com');
若 email 是唯一索引,且已有 'a@b.com' 记录,则两个事务都会尝试对已存在记录加 S 型 Next-Key Lock(用于冲突检测),随后又都试图插入新行 → 形成 X 与 S 锁互斥,死锁触发。
关键点:
-
INSERT ... ON DUPLICATE KEY UPDATE在唯一键冲突时,会加 X 型 Next-Key Lock,RC 下照常死锁 -
REPLACE INTO更激进:会在冲突记录及其下一条记录上都加 Next-Key Lock - 即使没显式事务,单条
INSERT也会开启隐式事务,锁在语句结束才释放 - 没有索引的列做唯一性判断?那会退化为全表扫描 + 大量 Record Lock,死锁风险反而更高
RC 配置后为什么业务还出问题
最常见原因是连接池或 ORM 没真正生效。MySQL 默认是 RR,很多 Java 应用用 HikariCP + MyBatis,连接从池中取出后,隔离级别沿用服务端默认值,SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED 并未执行。
验证是否生效的方法:
- 连接建立后立刻执行
SELECT @@transaction_isolation,不是看全局变量@@global.transaction_isolation - 检查慢查询日志里是否有
SELECT ... LOCK IN SHARE MODE类语句——这类语句在 RC 下虽不加 Gap Lock,但若没显式BEGIN,可能按会话默认 RR 执行 - binlog 格式必须是
ROW,否则 RC 下主从可能不一致;STATEMENT格式在 RC 下无法重放非确定性语句 - Spring 的
@Transactional(isolation = Isolation.READ_COMMITTED)能生效,但需确认数据源配置未覆盖该设置
RC 不是银弹:哪些场景它反而更危险
RC 对“先查后改”类逻辑极其不友好。比如库存扣减:SELECT stock FROM goods WHERE id = 123 得到 10,再 UPDATE goods SET stock = 9 WHERE id = 123 AND stock >= 10 —— 这中间窗口期,其他事务可能已把 stock 改为 9,导致超卖。
这时你不得不加 SELECT ... FOR UPDATE,而 RC 下它只锁行、不锁间隙,如果业务允许“幻读”,那没问题;但如果要防止新记录插入干扰判断(比如新增一个同规格商品),RC 就无能为力。
容易被忽略的细节:
- RC 下
SELECT ... FOR UPDATE不阻塞其他事务插入相邻间隙,所以“防插入”必须靠应用层或唯一约束兜底 - 长事务在 RC 下依然危险:MVCC 快照虽轻,但未提交事务仍会阻止 purge 线程清理 undo log,可能撑爆
ibdata1 - 某些 ORM 自动生成的批量更新语句,在 RC 下因 where 条件顺序不一致,仍可能因加锁顺序不同引发死锁
真正决定是否用 RC 的,不是并发数字,而是业务能否容忍不可重复读、幻读,以及有没有能力把校验逻辑收束到单条原子 SQL 里。











