READ COMMITTED 是多数业务的起点,因其避免不可重复读且不引入间隙锁,适合强时效性读操作;而 REPEATABLE READ 适用于需事务内多次读一致的场景,如银行转账。

READ COMMITTED 为什么是大多数业务的起点
MySQL 默认隔离级别是 REPEATABLE READ,但多数 Web 应用(尤其是读多写少、依赖应用层缓存或乐观锁的场景)实际更需要 READ COMMITTED。它避免了“不可重复读”,又不引入 REPEATABLE READ 下的间隙锁(Gap Lock)膨胀问题。
-
READ COMMITTED每次SELECT都读取最新已提交版本,适合订单状态轮询、库存查询等强时效性读操作 - 在高并发更新同一行时,
REPEATABLE READ可能因间隙锁导致死锁频发,而READ COMMITTED只加行锁,冲突面更小 - 注意:开启
binlog_format = ROW时,READ COMMITTED对 MVCC 版本链清理更积极,减少历史版本堆积
示例:电商下单前查库存,用 READ COMMITTED 能及时看到其他事务刚扣减的结果,避免超卖;若用 REPEATABLE READ,两次 SELECT 可能返回相同值,掩盖并发修改。
什么时候必须用 REPEATABLE READ
不是所有业务都能降级。当你的逻辑依赖「一次事务内多次读结果一致」且无法靠应用层兜底时,REPEATABLE READ 就成了事实上的底线。
- 典型场景:银行转账中先查余额、再校验、最后扣款——这三步必须看到同一份快照,否则可能绕过余额检查
-
SELECT ... FOR UPDATE在REPEATABLE READ下会锁住范围(间隙锁),防止幻读;但在READ COMMITTED下只锁命中行,幻读风险真实存在 - 如果业务已用
SELECT ... LOCK IN SHARE MODE做一致性读保护,又没配好索引,REPEATABLE READ的间隙锁可能意外锁住大片无关数据
常见错误:在 READ COMMITTED 下写「先查后更」逻辑,却没加 SELECT ... FOR UPDATE 或没做 version 字段校验,结果被并发覆盖。
SERIALIZABLE 是开关,不是选项
SERIALIZABLE 会让所有 SELECT 自动升级为 SELECT ... LOCK IN SHARE MODE,本质是用锁换隔离,性能断崖式下跌。
- 它解决的是极端一致性要求,比如金融核心账务日终跑批、跨库对账等离线强一致场景
- 在线交易系统启用
SERIALIZABLE后,简单SELECT也会阻塞写入,QPS 常掉 50% 以上 - MySQL 不支持真正的串行化调度(如 true serializable),
SERIALIZABLE仍可能因锁顺序引发死锁,需配合重试逻辑
别把它当成「最安全就最好」的默认项。线上环境一旦误设,监控里会立刻看到 Waiting for table metadata lock 或大量 Lock wait timeout exceeded。
怎么验证当前事务看到的到底是不是预期快照
光改配置不够,得确认行为是否符合预期。MySQL 提供了几个关键线索:
- 查看当前事务 ID:
SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID()(需有权限) - 检查是否启用 MVCC:执行
SELECT @@tx_isolation,确认返回值含READ-COMMITTED或REPEATABLE-READ,而非SESSION级临时覆盖 - 观察锁行为:用
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS和INNODB_LOCK_WAITS(8.0+ 已废弃,改用performance_schema.data_locks)看是否出现意料外的间隙锁
容易忽略的一点:Spring 的 @Transactional(isolation = Isolation.REPEATABLE_READ) 不会覆盖数据库全局设置,但会覆盖 JDBC 连接的 session 级设置——如果连接池里混用了不同隔离级别,行为会变得极难追踪。



















