文章目录
- 一、整体流程概述
- [二、Server 层:解析 SQL → 调用 InnoDB 执行更新](#二、Server 层:解析 SQL → 调用 InnoDB 执行更新)
- [三、InnoDB 查找记录(索引决定性能与锁)](#三、InnoDB 查找记录(索引决定性能与锁))
- 四、加锁:保证事务隔离性
- [五、写 Undo Log:记录旧值(用于 MVCC & 回滚)](#五、写 Undo Log:记录旧值(用于 MVCC & 回滚))
- [六、修改 Buffer Pool(内存)的数据页](#六、修改 Buffer Pool(内存)的数据页)
- [七、写 Redo Log(prepare 阶段)](#七、写 Redo Log(prepare 阶段))
- [八、写 Binlog:用于主从复制与恢复](#八、写 Binlog:用于主从复制与恢复)
- [九、两阶段提交:确保 Redo 和 Binlog 一致](#九、两阶段提交:确保 Redo 和 Binlog 一致)
- [十、释放锁 & 后台刷脏页](#十、释放锁 & 后台刷脏页)
- [十一、UPDATE 全流程总结](#十一、UPDATE 全流程总结)
一、整体流程概述
先给出一条 UPDATE 的"核心执行链路",便于你快速记住流程:
查找记录 → 加锁 → 写 undo → 修改内存页 → redo prepare → 写 binlog → redo commit → 释放锁
这条链路贯穿了 UPDATE 的所有关键机制,我们将按时间顺序深入讲解。
redolog与binlog为什么需要两阶段提交?
WAL(Write-Ahead Logging)机制详解:为什么数据库一定要"先写日志再写数据"?
二、Server 层:解析 SQL → 调用 InnoDB 执行更新
当客户端发送 SQL:
sql
UPDATE user SET age = 20 WHERE id = 1;
MySQL Server 做的工作包括:
- 解析 SQL(Parser)
- 优化器生成执行计划(Optimizer)
- 执行器运行执行计划(Executor)
执行器最终会调用 InnoDB:
handler->update_row()
真正的数据读写发生在 InnoDB,而 Server 层更多是调度者。
三、InnoDB 查找记录(索引决定性能与锁)
InnoDB 使用聚簇索引(B+Tree)查找目标行:
- 如果 WHERE 使用了索引 → 快速定位
- 如果没有索引 → 全表扫描,扫描过程中会导致大量锁竞争
找到记录后,数据页会被加载到 Buffer Pool(内存) 中。
这一步非常关键,因为:
- 是否使用索引决定了扫描开销
- 也决定了加锁范围(是否出现"锁表"效果)
四、加锁:保证事务隔离性
找到记录后,InnoDB 会加锁来保证并发事务的隔离性:
- 使用唯一索引时:行锁(Record Lock)
- 使用普通索引或无索引:Next-Key Lock(记录锁 + 间隙锁)
如果表没有索引,UPDATE 会扫描整个表:
可能会锁住大量记录,导致类似"表锁"的效果。
理解锁机制是排查 UPDATE 阻塞的关键。
五、写 Undo Log:记录旧值(用于 MVCC & 回滚)
在更新前,InnoDB 会生成 undo 记录:
text
旧值:age = 18
Undo 的作用:
- 事务回滚(恢复旧值)
- MVCC 快照读(构建历史版本链)
Undo 属于逻辑日志,记录的是"如何还原到更新前"。
六、修改 Buffer Pool(内存)的数据页
InnoDB 在内存中对数据页进行实际修改:
text
age = 20
此时发生了:
- 数据页被标记为 脏页(Dirty Page)
- 并未立即写回磁盘,刷盘由后台线程异步完成
此时 UPDATE 的"数据修改"已经发生,但事务还未正式提交。
七、写 Redo Log(prepare 阶段)
为了支持宕机恢复(Crash Recovery),InnoDB 会记录 redo:
page_id=xxx, offset=xxx, new_value=20
Redo 写入流程:
- 写入 redo log buffer(内存)
- 刷盘到 redo log file
- 标记为 prepare
此时 redo 已经持久化,但事务还未提交,仍有回滚可能。
八、写 Binlog:用于主从复制与恢复
Server 层写入 binlog:
update user set age=20 where id=1;
或 ROW 格式的事件。
Binlog 的关键作用:
- 主从复制
- 基于时间点恢复(PITR)
Binlog 也需要刷盘(fsync),否则可能造成主从不一致。
九、两阶段提交:确保 Redo 和 Binlog 一致
为了避免主库和从库数据不一致,MySQL 使用 Two Phase Commit(2PC):
阶段 1(prepare):
InnoDB 写 redo prepare(已持久化)
阶段 2(commit):
Server 写 binlog 成功后,通知 InnoDB:
可以提交了
InnoDB 将 redo 从 prepare → commit。
至此:
- redo 已提交
- binlog 已落盘
- 事务正式成功
这是 MySQL 保障一致性的核心机制。
十、释放锁 & 后台刷脏页
事务提交后:
- 锁被释放,其他事务可以访问记录
- 脏页由后台线程(checkpoint、page cleaner)异步刷盘
更新操作本身不会阻塞在磁盘 IO 上,这也是 MySQL 高性能的关键。
十一、UPDATE 全流程总结
SQL 解析 → 执行器调用 InnoDB
↓
索引查找 → 加锁
↓
生成 undo(保存旧值)
↓
修改内存页(产生脏页)
↓
redo prepare(写 redo)
↓
写 binlog
↓
redo commit(事务真正提交)
↓
释放锁 → 后台刷脏页