系统服务之间的数据同步,近期改动的时候,某个接口数据量上去,频繁发生死锁,同事排查后,发现是由于间隙锁导致,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 级别的特性)
- 性能提升: 没有了间隙锁,锁的粒度更小,并发吞吐量大幅提升。
- 死锁减少: 绝大多数由间隙锁导致的死锁都会消失。
- 出现幻读/不可重复读: 这是 RC 级别的“特性”,事务中两次查询的结果可能会不一致。
- 【核心要求】必须配合 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 是兼顾性能与一致性的主流架构方案,但它要求开发团队在应用层承担更多保障数据一致性的责任。