直接改用READ COMMITTED能显著提升高并发写入吞吐量,因其关闭间隙锁、仅对匹配行加行锁且语句执行完立即释放,避免RR级别下因间隙锁导致的范围锁定、锁冲突与死锁;但要求业务能容忍不可重复读。

直接改用 READ COMMITTED,能显著提升高并发写入吞吐量,前提是业务能接受“不可重复读”——这不是妥协,而是对 InnoDB 底层锁机制的合理让渡。
为什么 REPEATABLE READ 在高并发写入时容易卡住
MySQL 默认的 REPEATABLE READ 隔离级别在 InnoDB 中依赖间隙锁(Gap Lock)来缓解幻读。但间隙锁会锁定索引范围,不是单行;多个事务同时向同一索引区间(比如按时间戳插入、按 user_id 分片后批量写入)写入时,极易触发锁冲突甚至死锁。
典型表现包括:SHOW ENGINE INNODB STATUS 里频繁出现 lock wait,show processlist 中大量连接状态为 Updating 或 Locked,QPS 上不去但 CPU 和锁等待时间飙升。
- 间隙锁在 INSERT/UPDATE 操作中自动激活,无需显式加锁语句
- 即使 WHERE 条件命中唯一索引,InnoDB 仍可能加间隙锁(如二级索引 + 主键回表场景)
- RR 级别下,事务启动即生成 MVCC 快照,长期事务会拖慢 purge 线程,加剧 undo log 膨胀
READ COMMITTED 如何释放并发压力
READ COMMITTED 关闭间隙锁,MVCC 快照按语句粒度生成(每次 SELECT 都读最新已提交版本),写操作仅持行级锁到事务结束,不扩展范围。
这对高并发轨迹写入、日志落库、用户行为埋点等场景特别友好:插入不再被相邻 key 锁住,INSERT 多值语句能并行执行,死锁率大幅下降。
- 必须在
START TRANSACTION前设置,否则报错:Can’t change transaction isolation level when a transaction is active - Spring 的
@Transactional注解默认开启事务,所以不能在 service 方法里动态SET SESSION,得靠连接池initSql或 DataSource 初始化阶段注入 - 同一个事务内多次
SELECT可能返回不同结果,前端或业务逻辑需明确兜底(比如不依赖“两次查一样”,而用最后一次为准)
怎么安全地切到 READ COMMITTED
分两步走:先验证会话级生效,再决定是否全局持久化。别跳过验证——很多线上事故源于配置写错但没报错,只返回空字符串或 NULL。
查当前真实值:
SELECT @@session.transaction_isolation, @@global.transaction_isolation;
如果任一字段为 NULL,说明配置语法错误(比如用了下划线 transaction_isolation 写成 transaction_isolation,或引号/空格误入)。
- 临时切换(仅当前连接):
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED - 连接池初始化(如 HikariCP 的
connection-init-sql):SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED - 永久生效:在
my.cnf的[mysqld]段落加transaction-isolation = READ-COMMITTED(注意是连字符,不是下划线),然后重启 MySQL 进程 - 改完别忘了验证:新连接执行
SELECT @@transaction_isolation,确认返回'READ-COMMITTED'
哪些场景千万别切
不是所有读多写少都适合 READ COMMITTED。核心看业务是否隐式依赖“事务内一致性快照”。
例如金融记账类逻辑:先查余额,再判断是否足够,最后扣款——若中间余额被其他事务修改并提交,在 READ COMMITTED 下第二次查就会看到新值,但应用可能已基于旧值做了判断,导致超扣。
- 涉及“先读后写”且依赖读结果做决策的流程,必须保留
REPEATABLE READ或加SELECT ... FOR UPDATE - 报表类统计任务若要求“某时刻全库快照一致”,
READ COMMITTED不满足,得用REPEATABLE READ或显式事务 + 一致性读 - 使用了
binlog_format = STATEMENT且含不确定函数(如NOW())时,READ COMMITTED可能导致主从不一致,建议搭配ROW格式
真正难的不是改配置,而是厘清哪条 SQL 在哪个事务里依赖哪次读的结果——隔离级别调低后,问题不会立刻报错,而是以数据逻辑错乱的形式慢慢浮现。


















