够用,但仅限日志写入、报表统计等允许数据轻微偏差的场景;RC避免脏读且间隙锁更少、吞吐更高,但不解决不可重复读与幻读,资金/库存等强一致性场景必须用RR。

RC 级别在 MySQL 高并发下是否够用?
够用,但只在特定场景下——读已提交(READ-COMMITTED)能避免脏读,又比可重复读(REPEATABLE-READ)少加间隙锁,吞吐更高。但它不解决“不可重复读”和“幻读”,如果你的业务逻辑依赖同一事务内多次 SELECT 结果一致(比如先查余额再扣款),RC 就会出问题。
常见错误现象:UPDATE ... WHERE id = ? 在 RC 下可能更新到“刚被其他事务插入又提交”的行(因无间隙锁),导致业务逻辑漏判或重复处理。
- 适用场景:日志类写多读少、报表统计(允许轻微偏差)、状态流转类(如订单从 pending → processing,靠唯一约束+重试兜底)
- 不适用场景:资金账户、库存扣减、任何依赖“当前快照一致性”的校验逻辑
- MySQL 默认是
REPEATABLE-READ,切 RC 前必须确认应用层没隐式依赖 MVCC 快照行为
如何安全地把 MySQL 隔离级别设为 RC?
不能只改全局变量——客户端连接时的隔离级别以 session 级为准,且很多 ORM(如 MyBatis、Spring JDBC)会在连接初始化时显式执行 SET SESSION TRANSACTION ISOLATION LEVEL ...,覆盖全局设置。
实操建议:
- 服务启动前,在 MySQL 配置文件(
my.cnf)中加transaction_isolation = READ-COMMITTED(5.7.20+ 支持,旧版用tx_isolation) - 检查连接池配置:Druid 要设
initConnectionSqls=SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;HikariCP 无内置支持,需通过connection-init-sql - 验证是否生效:连上后执行
SELECT @@transaction_isolation,返回值应为READ-COMMITTED
RC 下为什么还会遇到死锁?
很多人误以为 RC 没间隙锁就“不会死锁”,其实不然。RC 仍会对 WHERE 条件命中(或扫描)的**已有记录**加行锁,只是不锁间隙。当两个事务按不同顺序更新同一组主键时,照样死锁。
典型例子:
-- 事务 A UPDATE t SET v = 1 WHERE id = 1; UPDATE t SET v = 2 WHERE id = 2; <p>-- 事务 B UPDATE t SET v = 3 WHERE id = 2; UPDATE t SET v = 4 WHERE id = 1;
这种交叉更新在 RC 和 RR 下都会死锁。区别在于:RR 下即使 id = 2 还不存在,也可能因间隙锁阻塞;RC 下只锁存在的行,所以冲突范围更小,但不等于没有。
- 排查方法:看
SHOW ENGINE INNODB STATUS中的LATEST DETECTED DEADLOCK段落 - 缓解手段:固定 DML 的 where 条件顺序(比如始终按
id升序更新)、拆大事务为小事务、用SELECT ... FOR UPDATE提前加锁并保持顺序
应用层该不该信任 RC 的“读一致性”?
不该。RC 的每次 SELECT 都读最新已提交版本,两次查询之间可能有其他事务提交了修改。这意味着你无法基于一次查询结果做后续判断——比如先 SELECT stock 判断够不够,再 UPDATE 扣减,中间 stock 可能已被别人改掉。
这不是 bug,是 RC 的设计本意。要解决它,得换思路:
- 用原子语句:例如
UPDATE product SET stock = stock - 1 WHERE id = ? AND stock >= 1,靠 MySQL 行锁 + 条件判断一步到位 - 加应用层重试:捕获
Deadlock found when trying to get lock或影响行为异常(如affected_rows == 0),回退重试 - 关键路径切 RR:不是全库切,而是对资金/库存等表,用
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ显式开启
真正难的从来不是设个隔离级别,而是厘清哪条 SQL 依赖哪类一致性保证——同一张表里,查日志用 RC,扣库存用 RR,往往共存。










