这个过程拆解为 "准备阶段"、"执行阶段"、"提交阶段(核心)" 和 "后台阶段" 四个部分。
假设执行 SQL:UPDATE user SET name = 'Li' WHERE id = 1;(原值为 'Wang')
第一阶段:准备与加载 (Buffer Pool)
连接器与解析:
MySQL Server 层收到 SQL,进行解析和权限校验。
执行器调用引擎:
执行器向 InnoDB 引擎索要 id=1 这一行数据。
加载到内存 (Buffer Pool):
InnoDB 检查 Buffer Pool(内存缓冲池)里有没有这就数据页。
如果有:直接返回。
如果没有:从磁盘(.ibd文件)加载该页到 Buffer Pool 中。
第二阶段:执行与日志记录 (Undo & Memory)
这是事务开始真正干活的阶段,所有操作都在内存中完成,还没碰磁盘数据文件。
写入 Undo Log (后悔药):
在修改数据前,InnoDB 会先把旧值('Wang')写入 Undo Log。
作用:如果事务失败回滚,或者通过 MVCC 进行快照读,需要靠它找回旧值。
更新内存 (Dirty Page):
在 Buffer Pool 中,将 id=1 的 name 修改为 'Li'。
此时,内存里的数据是新的,但磁盘上的数据还是旧的。这页数据被称为 "脏页" (Dirty Page)。
写入 Redo Log Buffer (崩溃恢复):
将这次物理修改("在第xx页第xx偏移量做了什么修改")写入内存中的 Redo Log Buffer。
注意:此时 Redo Log 还在内存里,还没落盘。
第三阶段:提交事务 (Commit & 2PC) ------ 最关键
当客户端发起 COMMIT 时,为了保证 Redo Log(引擎层) 和 Binlog(Server层) 的一致性,MySQL 使用了 两阶段提交 (Two-Phase Commit, 2PC)。
Redo Log 刷盘 (Prepare 状态):
InnoDB 将 Redo Log Buffer 里的内容写入磁盘的 Redo Log 文件中,并将状态标记为 PREPARE。
作用:也就是常说的 WAL(Write-Ahead Logging),先写日志,再写数据。只要 Redo Log 在,数据库宕机重启就能恢复数据。
写入 Binlog (归档):
MySQL Server 层将该操作的逻辑日志(SQL语句或行变更记录)写入磁盘的 Binlog 文件。
作用:用于主从复制和数据恢复。
Redo Log 提交 (Commit 状态):
InnoDB 收到 Binlog 写成功的通知后,再次操作 Redo Log,将状态改为 COMMIT。
至此,事务才算真正成功。
第四阶段:后台刷盘 (Checkpoint)
脏页刷盘:
此时,id=1 的数据在磁盘的数据文件(.ibd)里依然是旧值 'Wang'。
MySQL 的后台线程(Master Thread)会在系统空闲或内存不够时,将 Buffer Pool 中的 脏页 慢慢刷回到磁盘数据文件中。
这个过程叫 Checkpoint。
markup
[用户] UPDATE ... -> [Server层]
|
v
[InnoDB] 1. 加载数据页到 Buffer Pool
|
v
[InnoDB] 2. 写旧值到 Undo Log (为了回滚)
|
v
[InnoDB] 3. 更新 Buffer Pool 中的内存数据 (变成脏页)
|
v
[InnoDB] 4. 写新值到 Redo Log Buffer (内存)
|
v
[用户] COMMIT;
|
v
------------------ 两阶段提交 (2PC) 开始 ------------------
[InnoDB] 5. Redo Log 刷盘 (状态: PREPARE) <-- 崩溃恢复保障
|
[Server] 6. Binlog 刷盘 <-- 主从复制保障
|
[InnoDB] 7. Redo Log 更新 (状态: COMMIT)
------------------ 两阶段提交 (2PC) 结束 ------------------
|
v
[后台线程] 8. 慢慢把 Buffer Pool 的脏页刷到磁盘 (.ibd文件)
面试官可能会追问的深层问题:
Q1: 为什么要两阶段提交(2PC)?
回答:为了防止 Redo Log 和 Binlog 不一致。
如果先写 Redo Log 成功,Binlog 失败:主库重启后通过 Redo Log 恢复了数据,但 Binlog 里没这条数据,导致从库(备库)丢数据。
如果先写 Binlog 成功,Redo Log 失败:主库宕机重启后数据丢了,但从库通过 Binlog 同步了数据,导致主从不一致。
2PC 保证了要么两个都成功,要么都失败。
Q2: 既然 Redo Log 已经写磁盘了,为什么不直接把数据写到磁盘(.ibd)?
回答:这是为了性能。
写数据文件(.ibd)是随机写(Random I/O),因为数据分布在磁盘的不同位置,速度慢。
写 Redo Log 是顺序写(Sequential I/O),是追加写入,速度极快。
这就是 WAL (Write-Ahead Logging) 的核心思想:用顺序写代替随机写,提升并发能力。
Q3: 如果事务提交后,脏页还没刷盘,数据库宕机了怎么办?
回答:不用担心。重启时,InnoDB 会读取磁盘上的 Redo Log,把没刷盘的修改重新应用(Replay)到内存中,恢复出宕机前的数据状态。