READ COMMITTED 是大多数业务的合理起点,它关闭间隙锁、降低死锁概率,避免脏读,但允许同一事务中多次 SELECT 结果不同;需配合 ROW 格式 binlog 使用。

读已提交(READ COMMITTED)是大多数业务的合理起点
MySQL 默认隔离级别是 REPEATABLE READ,但它在并发更新场景下容易因间隙锁(gap lock)引发死锁或锁等待,尤其在非唯一条件查询 + UPDATE/DELETE 时。而 READ COMMITTED 关闭了间隙锁(仅在唯一索引等精确匹配时才加记录锁),大幅降低锁冲突概率,同时避免脏读,满足绝大多数 Web 应用的数据一致性要求。
实操建议:
- 确认业务能接受「同一事务中多次 SELECT 可能返回不同结果」——这是
READ COMMITTED的本质特征,比如订单状态页刷新看到新支付记录,属正常行为 - 使用
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED临时切换,或在my.cnf中全局配置:[mysqld] transaction_isolation = READ-COMMITTED
- 注意:Binlog 格式必须为
ROW(binlog_format = ROW),否则READ COMMITTED下某些语句可能无法正确复制
可重复读(REPEATABLE READ)只在强一致性场景下必要
REPEATABLE READ 保证事务内多次读取结果一致,但代价是 InnoDB 会使用间隙锁 + 行锁组合封锁索引范围,极易在高并发 INSERT/UPDATE 场景中触发死锁,尤其是对未命中索引的 WHERE 条件执行 UPDATE 时。
典型踩坑点:
- 表无合适索引,
UPDATE orders SET status = 'shipped' WHERE user_id = 123会锁全表或大范围间隙,阻塞其他用户下单 - 分页查询带
ORDER BY created_at LIMIT 10,若created_at无索引,InnoDB 可能锁住整个扫描范围 - 批量导入任务与前台更新共用同一张表,
REPEATABLE READ下锁升级风险显著高于READ COMMITTED
避免使用可串行化(SERIALIZABLE)
SERIALIZABLE 会让所有普通 SELECT 隐式升级为 SELECT ... LOCK IN SHARE MODE,导致读写互斥、并发能力断崖式下降。它不是“更安全”,而是用吞吐换隔离,实际生产中极少有业务真正需要这种级别。
除非你明确遇到以下情况,否则不要启用:
- 金融核心账务系统中,必须杜绝任何幻读且无法通过应用层重试+乐观锁解决
- 数据库本身不支持行级锁(如 MyISAM),但这种情况在 MySQL 8.0+ 已基本不存在
- 已确认所有慢查询都已优化、索引完备、连接池合理,但仍持续出现不可解释的幻读,且 DBA 已排除应用逻辑问题
配置方式:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;——仅作调试验证,切勿上线。
配置后必须验证锁行为是否符合预期
隔离级别只是 SQL 标准定义,具体锁策略由存储引擎实现。InnoDB 的行为和文档描述存在细微偏差,尤其在组合索引、NULL 值、隐式类型转换等边界场景下。
关键验证步骤:
- 用
SELECT * FROM performance_schema.data_locks查看当前事务持有的锁(需开启performance_schema并配置相关 consumer) - 在测试环境模拟并发 UPDATE,用
SHOW ENGINE INNODB STATUS\G检查LATEST DETECTED DEADLOCK区域 - 对比
READ COMMITTED和REPEATABLE READ下相同语句的EXPLAIN FORMAT=tree输出,确认是否因隔离级别变化导致执行计划退化
最常被忽略的一点:应用框架(如 Spring 的 @Transactional(isolation = ...))可能覆盖数据库配置,务必检查代码层是否显式指定了隔离级别并与其冲突。










