搞懂 MySQL 间隙锁:从原理到金融级选型

tianlong.xiang

系统服务之间的数据同步,近期改动的时候,某个接口数据量上去,频繁发生死锁,同事排查后,发现是由于间隙锁导致,MySQL 用的不多,记录下。

mysql 间隙锁,背景、原理,如何关闭间隙锁、关闭了会有什么问题;你是资深数据库 DBA,针对我的问题进行扩展分析补充。幻读是什么?主流大厂是选择 RC 还是 RR模式?我是金融行业,券商系统,你更建议我用什么方案?整理上述问题,输出文档。

在 MySQL 中,尤其是高并发场景下,“锁”是绕不过去的话题。间隙锁(Gap Lock)是 InnoDB 引擎中的一个核心特性,但也常常是性能瓶颈和死锁的根源。

这篇文章将帮你快速梳理间隙锁的核心、利弊,并为你(特别是金融行业)提供清晰的架构选型建议。

什么是间隙锁与幻读?

  • 幻读 (Phantom Read): 在一个事务中,你第一次查某范围(如 ID > 100)有 10 条数据,别人插入一条 ID=101 并提交后,你第二次查,发现变成了 11 条。这条“多出来的”新数据就像幻觉,故称“幻读”。

  • 间隙锁 (Gap Lock): MySQL 在 RR(可重复读) 隔离级别下,为了解决“幻读”问题而发明的。它不锁数据行,而是锁住数据之间的“缝隙”

    • 作用: 防止其他事务在这个“缝隙”中 INSERT 新数据。
    • 代价: 锁的范围变大,并发能力下降,容易死锁。

如何“关闭”间隙锁?

最直接的方法是将数据库隔离级别从 RR (Repeatable Read) 降为 RC (Read Committed)

-- 将当前会话隔离级别设为 RC
SET SESSION transaction_isolation = 'READ-COMMITTED';

-- 永久修改(需改 my.cnf 并重启)
-- [mysqld]
-- transaction-isolation = READ-COMMITTED

⚠️ 关闭的后果(即 RC 级别的特性)

  1. 性能提升: 没有了间隙锁,锁的粒度更小,并发吞吐量大幅提升。
  2. 死锁减少: 绝大多数由间隙锁导致的死锁都会消失。
  3. 出现幻读/不可重复读: 这是 RC 级别的“特性”,事务中两次查询的结果可能会不一致。
  4. 【核心要求】必须配合 ROW 格式 Binlog: 如果使用 RC 级别,必须binlog_format 设置为 ROW。否则,在主从复制时会因为缺少间隙锁的保护而导致主从数据不一致。

主流大厂选择:RC 还是 RR?

答案:绝大多数一线互联网大厂都选择 RC (Read Committed) + ROW Binlog 模式。

  • 原因: 互联网业务(如电商、社交)极端追求高并发。它们无法忍受 RR 间隙锁带来的频繁锁等待和死锁。
  • 权衡: 它们选择放弃数据库层面的“可重复读”,转而在应用层通过乐观锁(如版本号、CAS)等方式来保证关键业务(如库存、余额)的逻辑一致性。

金融行业(券商)如何选择?

对于数据一致性要求极高的券商系统,建议分场景治理

方案一:核心交易系统(高并发、低延迟)

建议:RC (Read Committed) + ROW Binlog + 应用层乐观锁

  • 场景: 订单撮合、下单、资金扣减。
  • 理由: 交易系统的并发压力不亚于互联网秒杀。RR 的间隙锁会成为性能瓶颈,导致严重的锁竞争甚至死锁,这是交易系统无法接受的。
  • 对策: 使用 RC 保证高性能,同时在应用代码中(如 Java)通过 UPDATE ... WHERE balance > ? 或 CAS 版本号机制来保证资金安全,防止超卖。

方案二:清算对账系统(批处理、高一致性)

建议:RR (Repeatable Read)

  • 场景: 盘后批量对账、生成报表、日终清算。
  • 理由: 批处理任务需要在一个“绝对一致”的数据快照上运行。它需要 RR 级别提供的能力,保证在整个统计过程中,数据不会发生“幻读”,确保总账能对平。

总结

隔离级别 优点 缺点 适用场景
RR (默认) 解决幻读,数据一致性高 并发低,易死锁 (间隙锁) 报表、清算、对数据一致性要求极高且并发不大的场景
RC 并发高,死锁少 会幻读、不可重复读 高并发核心业务 (如电商、金融交易),需配合 ROW Binlog

对于券商核心系统,RC + ROW Binlog 是兼顾性能与一致性的主流架构方案,但它要求开发团队在应用层承担更多保障数据一致性的责任。

金融IT程序员的瞎折腾、日常生活的碎碎念
使用 Hugo 构建
主题 StackJimmy 设计