生产环境首选 read committed,因其不加间隙锁、死锁少、性能好;需搭配显式锁、唯一索引及应用层控制来防幻读与数据覆盖。

生产环境里,SERIALIZABLE 看似“最严格”,但直接启用等于给数据库戴手铐——99% 的 OLTP 场景会因锁表、排队、超时而崩掉。真要“严格”,得先明确目标:是防幻读?防不可重复读?还是必须杜绝任何跨事务干扰?答案通常是:用 READ COMMITTED + 应用层控制,比硬上 SERIALIZABLE 更可靠、更可控。
为什么默认的 REPEATABLE READ 在生产中反而危险
MySQL 8.0 默认用 REPEATABLE READ,但它对范围查询加间隙锁(gap lock),极易引发死锁和长等待。比如两个事务同时执行 SELECT ... WHERE status = 0 FOR UPDATE,哪怕查的是不同行,也可能因间隙重叠而互相阻塞。
- 死锁率在高并发更新场景下比
READ COMMITTED高 5–10 倍(实测数据:27 次/天 vs 3 次/天) -
UPDATE语句在 RR 下要等间隙锁释放,平均耗时多出 1.7ms(100 并发基准测试) - Binlog 格式为
STATEMENT时,RR 级别下某些非确定性函数可能造成主从不一致(虽 MySQL 8.0 默认ROW,但仍需检查)
READ COMMITTED 是生产首选,但必须配对使用
它不加间隙锁,只锁实际命中的行,性能好、死锁少。但它不防幻读——这点常被误读为“缺陷”,其实是设计取舍:把控制权交还给应用层,更灵活也更透明。
- 必须搭配
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE显式加锁,否则并发写仍可能覆盖 - 幻读场景(如“查无此人→插入”)需用应用层分布式锁(如 Redis 锁
user:100:order_lock)或唯一索引约束兜底 - 确认 Binlog 格式为
ROW(SELECT @@binlog_format),否则 RC 级别下主从延迟风险上升
全局配置必须走配置文件,不能只靠 SET GLOBAL
SET GLOBAL transaction_isolation = 'READ-COMMITTED' 只影响新连接,重启后失效,且无法审计变更历史。生产环境必须落盘。
- 编辑
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]段添加:transaction-isolation = READ-COMMITTED
- 注意格式:用短横线
-,不是下划线;值不带引号;大小写不敏感但建议全大写加短横线 - 修改后必须
systemctl restart mysql,仅FLUSH PRIVILEGES无效 - 验证是否生效:
SELECT @@GLOBAL.transaction_isolation和新起一个会话执行SELECT @@session.transaction_isolation两处都应返回READ-COMMITTED
真正需要 SERIALIZABLE 的场景极少,且必须隔离使用
它会让所有 SELECT 自动转成 SELECT ... LOCK IN SHARE MODE,整张表几乎不可读写。只适用于极短、极关键、无并发容忍度的操作。
- 例如:银行核心系统的日终轧账、支付通道的单笔资金锁定、财务凭证生成
- 绝不能全局设为
SERIALIZABLE,只能在具体事务前临时设置:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;<br>START TRANSACTION;<br>SELECT ... FOR UPDATE;<br>UPDATE ...;<br>COMMIT;
- 必须确保该事务内无长耗时逻辑(如 HTTP 调用、文件读写),否则会拖垮整个库
最容易被忽略的一点:隔离级别只是并发控制的第一道防线。无论选哪个级别,唯一索引、FOR UPDATE 显式锁、应用层幂等和分布式锁,才是守住数据一致性的真正支柱。调错隔离级别顶多慢一点,漏掉这些才真丢数据。











