MySQL 事务隔离级别从脏读到串行化一篇讲透面试官“MySQL 的事务隔离级别有哪些默认是什么”你“四个级别读未提交、读已提交RC、可重复读RRMySQL 默认、串行化。它们分别解决脏读、不可重复读、幻读问题。”面试官“那 RR 级别下如何解决幻读和 RC 有什么区别”你“……”很多人能背出级别名称但一追问“MVCC 怎么实现”“间隙锁是什么”就含糊了。本文从理论基础到 InnoDB 实现彻底讲透事务隔离级别。一、事务的四大特性ACID在讲隔离级别之前先回顾事务的基本特性特性含义原子性Atomicity事务中的操作要么全部成功要么全部失败一致性Consistency事务前后数据状态保持一致隔离性Isolation多个事务并发执行时相互隔离不干扰持久性Durability事务提交后数据永久保存隔离级别就是用来控制隔离性的强弱程度。隔离级别越高数据一致性越好但并发性能越低。二、三个并发问题在并发事务中可能出现以下三种读问题问题含义示例脏读Dirty Read读到另一个事务未提交的数据事务A修改了数据但未提交事务B读取到然后事务A回滚B读到的是脏数据不可重复读Non-Repeatable Read同一事务内两次读取同一条记录结果不同因为其他事务更新了该记录事务A第一次读取某行值为10事务B修改为20并提交事务A再次读取变成20幻读Phantom Read同一事务内两次查询结果集的行数不同因为其他事务插入了新行事务A查询id10的记录有3条事务B插入一条id11并提交事务A再次查询变成4条注意区分不可重复读针对同一条记录的修改。幻读针对一批记录的插入或删除行数变化。三、四种隔离级别隔离级别脏读不可重复读幻读并发性能读未提交Read Uncommitted可能可能可能最高读已提交Read Committed不可能可能可能较高可重复读Repeatable Read默认不可能不可能理论上可能InnoDB通过间隙锁解决中等串行化Serializable不可能不可能不可能最低MySQL InnoDB 默认使用可重复读RR并且通过MVCC 间隙锁解决了幻读问题后面细说。四、各隔离级别详解1. 读未提交Read Uncommitted事务可以读取其他事务未提交的修改。这是最低级别几乎从不使用。问题脏读。示例-- 事务ASTARTTRANSACTION;UPDATEaccountSETbalancebalance-100WHEREid1;-- 未提交-- 事务B隔离级别读未提交STARTTRANSACTION;SELECTbalanceFROMaccountWHEREid1;-- 读到已扣减但未提交的值-- 如果事务A回滚事务B读到的就是脏数据适用场景无除非你能接受脏数据。2. 读已提交Read CommittedRC事务只能读取其他事务已经提交的修改。这是很多数据库如 PostgreSQL、Oracle的默认级别。解决的问题脏读。仍存在的问题不可重复读、幻读。示例不可重复读-- 事务ARC级别STARTTRANSACTION;SELECTbalanceFROMaccountWHEREid1;-- 100-- 此时事务B修改并提交-- 事务A再次查询SELECTbalanceFROMaccountWHEREid1;-- 可能变成90不可重复读InnoDB 实现通过MVCC多版本并发控制每次SELECT都会重新生成一个一致性视图read view所以能看到已提交的最新数据因此不可重复读无法避免。3. 可重复读Repeatable ReadRRMySQL InnoDB 的默认级别。保证在同一事务内多次读取同一条记录的结果一致。解决的问题脏读、不可重复读。幻读问题理论上幻读仍可能发生但 InnoDB 通过间隙锁Gap Lock或MVCC在大多数场景下避免了幻读。示例可重复读实现-- 事务ARR级别STARTTRANSACTION;SELECTbalanceFROMaccountWHEREid1;-- 100-- 事务B修改并提交UPDATEaccountSETbalance90WHEREid1;COMMIT;-- 事务A再次查询SELECTbalanceFROMaccountWHEREid1;-- 仍然是100可重复读InnoDB 实现原理事务第一次SELECT时生成一个read view包含未提交事务的 ID 列表。后续SELECT使用同一个 read view因此看不到其他事务提交的修改实现了可重复读。幻读的解决对于快照读普通SELECTMVCC 天然避免了幻读因为快照不变。对于当前读SELECT ... FOR UPDATE/UPDATE/DELETEInnoDB 使用间隙锁锁住查询范围内的间隙防止其他事务插入新行。-- 事务ASELECT*FROMtWHEREid10FORUPDATE;-- 事务B想插入 id11会被间隙锁阻塞从而避免幻读4. 串行化Serializable所有事务串行执行读操作也会加共享锁读锁写操作加排他锁。性能极低几乎只在数据一致性要求极高且并发极低的场景使用。解决的问题脏读、不可重复读、幻读全部解决。缺点并发能力几乎为零容易出现锁超时和死锁。五、MVCC 机制浅析MVCCMulti-Version Concurrency Control是 InnoDB 实现高并发隔离级别的核心。原理每行记录隐藏两个字段DB_TRX_ID最后修改该行的事务ID、DB_ROLL_PTR指向 undo log 中旧版本记录的指针。事务开始时生成一个read view包含当前活跃事务的 ID 列表未提交的。读取数据时根据 read view 判断哪个版本可见只看到事务 ID 小于当前事务且不在活跃列表中的版本。不同隔离级别下的 read view 生成时机RC每条SELECT语句都生成一个新的 read view所以能看到已提交的最新数据。RR第一条SELECT生成 read view整个事务共用同一个因此可重复读。六、间隙锁Gap Lock与幻读在 RR 级别下InnoDB 通过间隙锁来防止幻读。间隙锁锁住索引记录之间的“间隙”防止其他事务在间隙中插入新记录。示例-- 表 t 有 id 列主键已有记录 id10, 20-- 事务ASELECT*FROMtWHEREidBETWEEN10AND20FORUPDATE;-- 此时 InnoDB 不仅锁住 id10 和 20 的记录还锁住 (10,20) 之间的间隙-- 事务B 想插入 id15会被阻塞直到事务A提交注意如果查询使用唯一索引等值查询且记录存在则只锁记录不锁间隙不会产生幻读风险。如果查询使用普通索引或范围查询会锁间隙。串行化级别也会加间隙锁但还会对读操作加共享锁。七、如何查看和设置隔离级别-- 查看当前会话隔离级别SELECTtransaction_isolation;-- MySQL 8.0SELECTtx_isolation;-- MySQL 5.7-- 查看全局隔离级别SELECTglobal.transaction_isolation;-- 设置会话隔离级别SETSESSIONTRANSACTIONISOLATIONLEVELREADCOMMITTED;-- 设置全局隔离级别需要重启或重新连接生效SETGLOBALTRANSACTIONISOLATIONLEVELREPEATABLEREAD;八、常见面试追问Q1MySQL 默认隔离级别是 RR为什么很多互联网公司改成 RCRC 的锁机制更简单没有间隙锁并发性能更高尤其在主从复制中RC binlog_formatROW 可以避免间隙锁带来的死锁问题。RC 允许不可重复读但在大多数业务场景如订单、支付中单条查询通常不需要可重复读通过业务逻辑或乐观锁即可保证一致性。但要注意RC 下可能出现不可重复读和幻读需要业务层面接受或使用其他手段。Q2RR 级别下能完全避免幻读吗普通快照读不加锁的SELECT不会出现幻读因为快照不变。当前读SELECT ... FOR UPDATE/UPDATE/DELETE通过间隙锁避免了幻读。但有一种情况例外事务中先快照读再当前读可能会看到不同的行数因为当前读会读取最新提交的数据。但这不属于严格意义上的幻读幻读定义是同一查询两次结果集不同但这里查询方式不同。一般面试中认为 RR 级别下 InnoDB 解决了幻读。Q3MVCC 和锁的关系MVCC 用于快照读普通SELECT实现无锁并发。锁行锁、间隙锁用于当前读SELECT ... FOR UPDATE、UPDATE、DELETE保证写操作的正确性。Q4如何模拟幻读在 RC 级别下很容易模拟幻读-- 事务ARCSTARTTRANSACTION;SELECT*FROMtWHEREid10;-- 假设返回2行-- 事务B插入 id11 并提交INSERTINTOtVALUES(11);COMMIT;-- 事务A再次查询SELECT*FROMtWHEREid10;-- 返回3行幻读在 RR 级别下如果使用当前读间隙锁会阻塞插入模拟需要一些技巧如先快照读再当前读。Q5隔离级别对性能的影响读未提交无锁性能最高但数据不可靠。读已提交锁范围小性能较好。可重复读加间隙锁可能导致更多锁冲突性能中等。串行化大量锁等待性能最差。九、总结隔离级别脏读不可重复读幻读实现方式InnoDB读未提交可能可能可能无锁直接读最新版本读已提交不可能可能可能每条SELECT生成read view可重复读不可能不可能通常不可能间隙锁第一条SELECT生成read view当前读加间隙锁串行化不可能不可能不可能读加共享锁写加排他锁事务串行一句话记住隔离级别读未提交脏数据读已提交不脏但幻不可重可重复读默认防幻串行化性能最差。理解事务隔离级别是数据库优化和面试的基础。希望这篇文章能帮你彻底掌握 MySQL 事务隔离的核心知识欢迎继续讨论。