MySQL 事务隔离级别详解
MySQL 事务隔离级别详解
一、为什么要了解隔离级别
事务的原子性、一致性由锁和隔离级别共同保证。当多个事务并发读写同一份数据时,如果隔离级别设置不当,就会出现脏读、不可重复读、幻读三类问题。PHP 开发订单扣库存、余额转账、排行榜统计这类并发业务,经常遇到重复扣减、读到旧数据的问题,根源往往就是隔离级别理解不到位。
MySQL InnoDB 提供四个标准隔离级别,隔离强度依次增强,性能依次下降,需要根据业务权衡。
二、四种隔离级别
1. READ UNCOMMITTED(读未提交)
事务可以读到其他事务还未提交的数据,会出现脏读。几乎没有业务使用,性能最好但一致性最差,一般只用于压测。
2. READ COMMITTED(读已提交)
只能读到其他事务已经提交的数据,解决脏读。但同一事务内两次读取同一行,可能读到不同结果,这就是不可重复读。Oracle、PostgreSQL 默认级别。
3. REPEATABLE READ(可重复读,MySQL 默认)
同一事务内多次读取同一行结果一致,解决不可重复读。InnoDB 通过 MVCC 快照实现,普通查询读快照。但仍可能出现幻读:两次范围查询返回的行数不同,InnoDB 在当前读(加锁查询)下用间隙锁尽量规避。
4. SERIALIZABLE(串行化)
所有事务串行执行,完全隔离,解决幻读,但并发性能最差,几乎不用于高并发业务。
三、三种并发异常
- 脏读:读到别的事务未提交的数据,对方回滚后数据作废;
- 不可重复读:同一事务两次读同一行,值被其他事务修改并提交;
- 幻读:同一事务两次范围查询,行数因为别的事务插入/删除而变化。
四、查看与设置隔离级别
-- 查看全局和会话隔离级别
SELECT @@global.transaction_isolation;
SELECT @@session.transaction_isolation;
-- 设置会话级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
my.cnf 全局配置:
transaction-isolation = REPEATABLE-READ
五、MVCC 与 ReadView
可重复读靠 MVCC(多版本并发控制)实现。每行保存多个版本,事务启动时生成 ReadView,普通 SELECT 根据 ReadView 判断可见版本,读取快照而不是最新行,因此不会被其他事务提交影响。当前读(SELECT ... FOR UPDATE、UPDATE、DELETE)走最新版本并加锁。
六、PHP 业务选型建议
- 普通查询、后台列表:默认 REPEATABLE READ 足够;
- 对账、统计报表:可提升到 READ COMMITTED,减少长事务持锁;
- 强一致资金操作:配合乐观锁/悲观锁,不要单纯依赖隔离级别;
- 避免长事务:事务内不要调用外部 HTTP 接口、不要循环大量更新,长时间持有锁会引发阻塞死锁。
七、常见误区
- 以为调高隔离级别就能解决所有并发问题,忽略锁和业务设计;
- 事务内混入远程请求,锁长时间不释放,导致连接堆积;
- 幻读只在范围查询出现,单行更新不会幻读;
- 把隔离级别和事务锁混为一谈,二者共同生效。
八、总结
隔离级别是在一致性和并发之间取舍的开关。理解脏读、不可重复读、幻读的区别,看懂 InnoDB MVCC 快照原理,才能在订单、库存、余额这类并发业务里选择合适级别。生产环境保持默认可重复读,重点优化事务长度,而不是盲目拉高隔离等级。