MySQL 是如何实现事务的?
MySQL 事务的实现本质上是“日志先行”(Write-Ahead Logging, WAL)与“版本管理”的结合。我们分维度深度拆解:
一、 原子性(Atomicity)的实现:Undo Log(回滚日志)
原子性要求事务中的操作要么全做,要么全不做。
- 物理原理:当事务对数据库进行修改时,InnoDB 会先记录对应的 Undo Log。
- 如果你 INSERT 了一条记录,Undo Log 会记一条对应的 DELETE。
- 如果你 UPDATE 了一行,Undo Log 会记下修改前的值。
- 逻辑实现:如果事务执行失败或调用了 ROLLBACK,InnoDB 会逻辑地执行 Undo Log 中的逆操作,将数据恢复到事务开始前的状态。
二、 持久性(Durability)的实现:Redo Log(重做日志)
持久性保证一旦事务提交,数据就不会丢失,即使数据库宕机。
- 痛点:如果每次提交都把数据页同步刷新到磁盘(随机 IO),性能极差。
- WAL 机制:InnoDB 采用“日志先行”策略。事务修改数据时,先写内存缓存(Buffer Pool),并同步将修改记录写入 Redo Log(顺序 IO)。
- 崩溃恢复(Crash Recovery):如果数据库在数据页还没来得及刷盘时宕机,重启后 InnoDB 会读取 Redo Log,把未落盘的数据重做一遍,确保提交的数据不丢失。
三、 隔离性(Isolation)的实现:锁 + MVCC
这是最复杂的部分,MySQL 通过“两条腿走路”来实现并发控制:
1. MVCC(多版本并发控制)—— 实现“读-写”并行
MVCC 让“读”操作不需要加锁,极大地提升了并发性能。
- 版本链:每行记录都有隐藏列(TRX_ID 事务 ID 和 ROLL_PTR 回滚指针)。修改数据时,旧版本会存在 Undo Log 里形成一条链。
- ReadView(一致性视图):
- 在 RC(读已提交) 级别下,每次 SELECT 都会生成一个新的 ReadView。
- 在 RR(可重复读) 级别下,整个事务期间只在第一次 SELECT 生成 ReadView。
- 可见性算法:通过比较当前事务 ID 与 ReadView 中的活跃事务 ID,判断应该读取版本链中的哪一个快照。
2. 锁机制(Locking)—— 实现“写-写”串行
当两个事务同时修改同一行时,必须通过锁来排队。
- 记录锁(Record Lock):锁住具体的索引记录。
- 间隙锁(Gap Lock)/ 临键锁(Next-Key Lock):这是 InnoDB 在 RR 级别下防止幻读的关键。它不仅锁住记录,还锁住记录之间的间隙,防止其他事务插入新数据。
四、 一致性(Consistency)的实现:最终目的
一致性是事务追求的最终状态,它是通过原子性、持久性和隔离性共同保障的。
- 双写缓冲(Doublewrite Buffer):防止物理页写碎导致的数据损坏。
- 两阶段提交(Two-Phase Commit):确保 Redo Log 和 Binlog 的数据一致。
- Prepare 阶段:写入 Redo Log,事务状态设为 Prepare。
- Commit 阶段:写入 Binlog,最后将 Redo Log 状态设为 Commit。
- 意义:防止出现“日志记录了执行,但主从同步的 Binlog 没记录”的情况。
总结:底层逻辑链路
- 为了不丢数据:引入 Redo Log(持久性)。
- 为了能后悔:引入 Undo Log(原子性)。
- 为了高并发读:引入 MVCC(基于 Undo Log 版本链)。
- 为了防乱改:引入 锁机制(隔离性)。
- 为了主从一致:引入 两阶段提交。
MySQL知识点整理 文章被收录于专栏
经典MySQL知识
查看18道真题和解析