在上一篇博客中我们已经学习了事务的 ACID 特性四种隔离级别Read UncommittedRead Committed并且知道Read Committed 会出现“不可重复读”问题而 MySQL 默认隔离级别REPEATABLE READ却能解决这个问题。那么MySQL 是怎么做到的为什么普通 SELECT 不加锁也能保证一致性MVCC 到底是什么Read View 是什么幻读为什么难解决这一篇我们彻底搞懂。一、Repeatable Read可重复读Repeatable Read可重复读简称RR这是MySQL默认隔离级别面试高频核心MVCC最重要的应用场景RR 的核心目标保证同一个事务中多次读取同一数据结果必须一致二、Read Committed 为什么不行在 Read Committed 下事务ABEGIN; SELECT balance FROM account WHERE id 1;结果100事务BUPDATE account SET balance 200 WHERE id 1; COMMIT;事务A再次读取SELECT balance FROM account WHERE id 1;结果200同一个事务两次读取结果不同。这就是不可重复读原因因为RC 每次 SELECT 都读取“最新已提交数据”。所以别人提交之后当前事务立刻就能看到。三、Repeatable Read 如何解决RR 的核心思想第一次读取时生成一个“数据快照”。之后不再读取最新数据而是读取事务开始时看到的数据因此即使别人修改并提交当前事务依然读取旧值。四、RR实验演示事务ABEGIN; SELECT balance FROM account WHERE id 1;结果100事务BUPDATE account SET balance 200 WHERE id 1; COMMIT;事务A再次读取SELECT balance FROM account WHERE id 1;结果仍然100为什么因为RR读取的不是最新数据。而是“事务开始时的数据快照”五、MVCC多版本并发控制RR 能实现的核心MVCC全称Multi-Version Concurrency Control即多版本并发控制六、为什么需要 MVCC假设数据库只有一份数据balance 100事务A正在读取。此时事务B修改balance 200那么事务A就无法继续读取旧值。于是MySQL想到一个办法不直接覆盖旧数据。而是保留多个版本七、MVCC 的核心思想假设最开始100之后有人修改200再修改300实际上数据库内部更像300 ↓ 200 ↓ 100形成版本链不同事务读取不同版本。八、MVCC 的优点1. 普通SELECT不加锁例如SELECT * FROM account;不会阻塞写操作。2. 写操作也不阻塞读相比传统读加锁 写加锁MVCC并发能力高很多。3. 实现可重复读事务始终读取自己事务开始时的版本。九、隐藏字段InnoDB 每一行数据后面实际上隐藏了几个字段。虽然我们平时看不到。但它们非常重要。1. trx_id表示最后一次修改该行的事务ID例如事务10修改了该行那么trx_id 102. roll_pointer指向undo log即旧版本数据。十、undo log回滚日志undo log回滚日志是MVCC真正保存历史版本的位置。十一、undo log 的作用1. 事务回滚例如ROLLBACK;数据库需要恢复旧值。2. MVCC读取历史版本事务读取快照时实际上会沿着undo log查找旧版本十二、版本链假设数据经历100 - 200 - 300内部实际上300 ↓ 200 ↓ 100通过roll_pointer串起来。这就形成版本链十三、Read View读视图MVCC 中最核心的概念Read View一、什么是 Read View可以理解为当前事务观察数据库的“视角”。二、什么时候生成在RR下第一次 SELECT 时生成。之后整个事务复用同一个 Read View。三、作用决定哪些版本当前事务可以看到十四、Read View 的简单理解事务启动时数据库会记录当前有哪些事务正在活跃之后判断某个数据版本是否对当前事务可见举个简单例子假设当前事务5正在运行 事务6正在运行 事务7正在运行事务8开始读取。那么Read View 会记录[5,6,7]之后如果某数据是事务6修改的但事务6还没提交。那么事务8不能看到。十五、快照读 与 当前读1. 快照读Snapshot Read普通 SELECTSELECT * FROM account;特点使用MVCC不加锁读取历史快照2. 当前读Current Read读取最新数据并加锁。例如SELECT * FROM account FOR UPDATE;或者UPDATE ... DELETE ... INSERT ...这些都属于当前读当前读特点读取最新版本会加锁可能阻塞十六、为什么 RR 能读到自己修改的数据这是很多人第一次学MVCC时最困惑的点。事务ABEGIN; SELECT balance FROM account WHERE id 1;结果100事务A自己修改UPDATE account SET balance 500 WHERE id 1;再次查询SELECT balance FROM account WHERE id 1;结果500不是说RR读取旧快照吗为什么又变成500了原因因为事务永远能看到自己修改的数据。MySQL 会特殊处理自己写的数据对自己始终可见十七、幻读Phantom Read幻读同一个事务中两次查询的数据“数量”不一致。示例事务ABEGIN; SELECT * FROM student WHERE age 18;结果3条数据事务BINSERT INTO student VALUES(...,18); COMMIT;事务A再次查询SELECT * FROM student WHERE age 18;结果4条数据像突然“冒出来”一条。因此幻读十八、MySQL 如何解决幻读理论上RR 不能完全解决幻读。但是InnoDB 对幻读做了很强的优化。核心Next-Key Lock临键锁十九、Next-Key Lock临键锁本质行锁 间隙锁不仅锁当前行。还锁数据之间的间隙为什么锁间隙为了防止别人插入新数据从而避免幻读。二十、Serializable串行化最高隔离级别SERIALIZABLE特点所有事务排队执行类似单线程优点不会出现脏读不可重复读幻读缺点性能最差。并发能力最低。因此生产环境极少使用。二十一、四种隔离级别总结隔离级别脏读不可重复读幻读Read Uncommitted会会会Read Committed不会会会Repeatable Read不会不会基本不会Serializable不会不会不会二十二、为什么 MySQL 默认选择 RR原因1. 一致性更强相比RC避免不可重复读数据更稳定2. MVCC性能优秀虽然隔离更强。但是普通SELECT不加锁因此性能仍然很高。3. InnoDB优化了幻读问题通过MVCCNext-Key Lock实现高隔离 高性能二十三、事务底层原理总结事务底层最核心一、原子性通过undo log实现回滚。二、持久性通过redo log保证崩溃不丢数据。三、隔离性通过锁MVCC实现。四、一致性由ACID共同保证最终实现。二十四、总结事务真正解决的问题高并发下的数据一致性MySQL通过undo logredo logMVCCRead ViewNext-Key Lock锁机制最终实现高并发下的数据安全与高性能好啦这就是MySQL里面关于事务的知识点啦接下来我们马上学习视图再接入我们的项目就算学完MySQL啦~~
MySQL事务(二)
发布时间:2026/7/17 0:38:27
在上一篇博客中我们已经学习了事务的 ACID 特性四种隔离级别Read UncommittedRead Committed并且知道Read Committed 会出现“不可重复读”问题而 MySQL 默认隔离级别REPEATABLE READ却能解决这个问题。那么MySQL 是怎么做到的为什么普通 SELECT 不加锁也能保证一致性MVCC 到底是什么Read View 是什么幻读为什么难解决这一篇我们彻底搞懂。一、Repeatable Read可重复读Repeatable Read可重复读简称RR这是MySQL默认隔离级别面试高频核心MVCC最重要的应用场景RR 的核心目标保证同一个事务中多次读取同一数据结果必须一致二、Read Committed 为什么不行在 Read Committed 下事务ABEGIN; SELECT balance FROM account WHERE id 1;结果100事务BUPDATE account SET balance 200 WHERE id 1; COMMIT;事务A再次读取SELECT balance FROM account WHERE id 1;结果200同一个事务两次读取结果不同。这就是不可重复读原因因为RC 每次 SELECT 都读取“最新已提交数据”。所以别人提交之后当前事务立刻就能看到。三、Repeatable Read 如何解决RR 的核心思想第一次读取时生成一个“数据快照”。之后不再读取最新数据而是读取事务开始时看到的数据因此即使别人修改并提交当前事务依然读取旧值。四、RR实验演示事务ABEGIN; SELECT balance FROM account WHERE id 1;结果100事务BUPDATE account SET balance 200 WHERE id 1; COMMIT;事务A再次读取SELECT balance FROM account WHERE id 1;结果仍然100为什么因为RR读取的不是最新数据。而是“事务开始时的数据快照”五、MVCC多版本并发控制RR 能实现的核心MVCC全称Multi-Version Concurrency Control即多版本并发控制六、为什么需要 MVCC假设数据库只有一份数据balance 100事务A正在读取。此时事务B修改balance 200那么事务A就无法继续读取旧值。于是MySQL想到一个办法不直接覆盖旧数据。而是保留多个版本七、MVCC 的核心思想假设最开始100之后有人修改200再修改300实际上数据库内部更像300 ↓ 200 ↓ 100形成版本链不同事务读取不同版本。八、MVCC 的优点1. 普通SELECT不加锁例如SELECT * FROM account;不会阻塞写操作。2. 写操作也不阻塞读相比传统读加锁 写加锁MVCC并发能力高很多。3. 实现可重复读事务始终读取自己事务开始时的版本。九、隐藏字段InnoDB 每一行数据后面实际上隐藏了几个字段。虽然我们平时看不到。但它们非常重要。1. trx_id表示最后一次修改该行的事务ID例如事务10修改了该行那么trx_id 102. roll_pointer指向undo log即旧版本数据。十、undo log回滚日志undo log回滚日志是MVCC真正保存历史版本的位置。十一、undo log 的作用1. 事务回滚例如ROLLBACK;数据库需要恢复旧值。2. MVCC读取历史版本事务读取快照时实际上会沿着undo log查找旧版本十二、版本链假设数据经历100 - 200 - 300内部实际上300 ↓ 200 ↓ 100通过roll_pointer串起来。这就形成版本链十三、Read View读视图MVCC 中最核心的概念Read View一、什么是 Read View可以理解为当前事务观察数据库的“视角”。二、什么时候生成在RR下第一次 SELECT 时生成。之后整个事务复用同一个 Read View。三、作用决定哪些版本当前事务可以看到十四、Read View 的简单理解事务启动时数据库会记录当前有哪些事务正在活跃之后判断某个数据版本是否对当前事务可见举个简单例子假设当前事务5正在运行 事务6正在运行 事务7正在运行事务8开始读取。那么Read View 会记录[5,6,7]之后如果某数据是事务6修改的但事务6还没提交。那么事务8不能看到。十五、快照读 与 当前读1. 快照读Snapshot Read普通 SELECTSELECT * FROM account;特点使用MVCC不加锁读取历史快照2. 当前读Current Read读取最新数据并加锁。例如SELECT * FROM account FOR UPDATE;或者UPDATE ... DELETE ... INSERT ...这些都属于当前读当前读特点读取最新版本会加锁可能阻塞十六、为什么 RR 能读到自己修改的数据这是很多人第一次学MVCC时最困惑的点。事务ABEGIN; SELECT balance FROM account WHERE id 1;结果100事务A自己修改UPDATE account SET balance 500 WHERE id 1;再次查询SELECT balance FROM account WHERE id 1;结果500不是说RR读取旧快照吗为什么又变成500了原因因为事务永远能看到自己修改的数据。MySQL 会特殊处理自己写的数据对自己始终可见十七、幻读Phantom Read幻读同一个事务中两次查询的数据“数量”不一致。示例事务ABEGIN; SELECT * FROM student WHERE age 18;结果3条数据事务BINSERT INTO student VALUES(...,18); COMMIT;事务A再次查询SELECT * FROM student WHERE age 18;结果4条数据像突然“冒出来”一条。因此幻读十八、MySQL 如何解决幻读理论上RR 不能完全解决幻读。但是InnoDB 对幻读做了很强的优化。核心Next-Key Lock临键锁十九、Next-Key Lock临键锁本质行锁 间隙锁不仅锁当前行。还锁数据之间的间隙为什么锁间隙为了防止别人插入新数据从而避免幻读。二十、Serializable串行化最高隔离级别SERIALIZABLE特点所有事务排队执行类似单线程优点不会出现脏读不可重复读幻读缺点性能最差。并发能力最低。因此生产环境极少使用。二十一、四种隔离级别总结隔离级别脏读不可重复读幻读Read Uncommitted会会会Read Committed不会会会Repeatable Read不会不会基本不会Serializable不会不会不会二十二、为什么 MySQL 默认选择 RR原因1. 一致性更强相比RC避免不可重复读数据更稳定2. MVCC性能优秀虽然隔离更强。但是普通SELECT不加锁因此性能仍然很高。3. InnoDB优化了幻读问题通过MVCCNext-Key Lock实现高隔离 高性能二十三、事务底层原理总结事务底层最核心一、原子性通过undo log实现回滚。二、持久性通过redo log保证崩溃不丢数据。三、隔离性通过锁MVCC实现。四、一致性由ACID共同保证最终实现。二十四、总结事务真正解决的问题高并发下的数据一致性MySQL通过undo logredo logMVCCRead ViewNext-Key Lock锁机制最终实现高并发下的数据安全与高性能好啦这就是MySQL里面关于事务的知识点啦接下来我们马上学习视图再接入我们的项目就算学完MySQL啦~~