READ COMMITTED 是多数业务的合理起点,它避免间隙锁、降低死锁风险,适用于允许不可重复读的场景;仅当强一致性必需(如银行余额校验)时才用 REPEATABLE READ;SERIALIZABLE 基本不用。

READ COMMITTED 是大多数业务的合理起点
MySQL 默认隔离级别是 REPEATABLE READ,但它在可重复读语义下使用间隙锁(gap lock)来防止幻读,这会显著增加锁冲突概率,尤其在高并发 INSERT ... SELECT 或范围 UPDATE 场景下容易触发死锁或锁等待超时(Lock wait timeout exceeded)。而 READ COMMITTED 下只对已存在的记录加行锁,不加间隙锁,锁粒度更小、并发更高。
适用场景包括:电商订单支付状态更新、用户积分变动、日志类写入等——只要业务能接受“同一事务中两次相同 SELECT 可能返回不同结果”(即允许不可重复读),就值得切换。
- 确认当前级别:
SELECT @@transaction_isolation; - 临时修改会话级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; - 全局修改需谨慎:在
my.cnf中设transaction_isolation = 'READ-COMMITTED',但要同步评估所有存量 SQL 是否依赖可重复读语义(如基于首次查询结果做二次判断的逻辑)
什么时候必须用 REPEATABLE READ
只有当业务逻辑明确依赖“事务内多次读取一致”且无法通过应用层加锁或重试规避时,才保留 REPEATABLE READ。典型例子是银行转账中的余额校验:先查账户余额,再判断是否足够扣款,整个过程不能被其他事务的中间更新干扰。
但要注意:MySQL 的 REPEATABLE READ 并非标准 SQL 实现,它用 MVCC + 间隙锁模拟可重复读,副作用是范围查询(如 SELECT * FROM t WHERE id > 100)会锁住 100 之后的所有间隙,哪怕那些行还不存在。这常导致插入新记录时被阻塞。
- 避免全表扫描式范围条件,尽量让
WHERE走索引,缩小间隙锁覆盖范围 - 如果只是想防止幻读但不需要强一致性,考虑用
SELECT ... FOR UPDATE显式加锁替代隔离级别依赖 - 升级到 MySQL 8.0+ 后,可结合
innodb_lock_wait_timeout和应用层重试,降低对REPEATABLE READ的强依赖
Serializable 基本不用,除非你真需要串行化
SERIALIZABLE 会让所有普通 SELECT 隐式转为 SELECT ... LOCK IN SHARE MODE,相当于给每条读语句加共享锁。它彻底消除幻读,但也几乎消灭并发——读写互斥、写写互斥,QPS 断崖式下跌。线上服务极少启用。
真正需要串行执行的逻辑(如库存扣减+生成单号+写日志这一整套),应该由应用层通过分布式锁或数据库唯一约束(如用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE)控制,而不是靠隔离级别硬扛。
- 测试阶段偶尔开启用于复现竞态问题,但上线前务必关掉
- 如果误开,监控会明显看到
Innodb_row_lock_waits和Threads_running异常升高 - 它不能替代应用层幂等设计,反而会掩盖真实的数据竞争点
别忽略 autocommit 和显式事务边界的影响
隔离级别只在事务内生效。很多并发问题其实不是隔离级别选错,而是没正确开启事务:autocommit=1 下每个语句自成事务,SELECT 看到的是语句开始时刻的快照,但两次 SELECT 之间可能已被其他事务修改——这和隔离级别无关,是事务使用方式问题。
例如库存扣减逻辑写成两个独立语句:SELECT stock FROM item WHERE id=123; → 应用判断够用 → UPDATE item SET stock=stock-1 WHERE id=123;,即使在 REPEATABLE READ 下也存在竞态。必须包在 BEGIN; ... COMMIT; 中,并配合 SELECT ... FOR UPDATE。
- 检查
autocommit状态:SELECT @@autocommit; - 批量操作务必显式
BEGIN,避免隐式事务拆分 - ORM 框架(如 MyBatis、Django ORM)默认事务行为差异大,需确认其
@Transactional注解实际生效范围
实际调优时,隔离级别只是并发控制拼图的一块。真正棘手的往往是长事务、未提交的写锁、缺乏索引导致的锁升级,或者应用层把本该原子的操作拆成了多步。先看 SHOW ENGINE INNODB STATUS\G 里的 TRANSACTIONS 和 LOCK WAIT 段,比盲目调隔离级别管用得多。










