MySQL 中 UPDATE 语句的执行过程全解析

文章目录

  • 一、整体流程概述
  • [二、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 做的工作包括:

  1. 解析 SQL(Parser)
  2. 优化器生成执行计划(Optimizer)
  3. 执行器运行执行计划(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 写入流程:

  1. 写入 redo log buffer(内存)
  2. 刷盘到 redo log file
  3. 标记为 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(事务真正提交)
      ↓
释放锁 → 后台刷脏页
相关推荐
数智化管理手记33 分钟前
主数据重复、错漏频发?一站式主数据管理平台如何落地?
大数据·运维·数据库·人工智能·数据挖掘
吴声子夜歌37 分钟前
MongoDB 4.2——选择片键
数据库·mongodb
天天进步一点点1 小时前
QSqlQuery删除sqlite数据库执行drop数据表报错database table is locked unable to fetch row问题
数据库·oracle·sqlite
音视频工程实战1 小时前
PromptQL 新手入门与实战指南
数据库·sql·算法
清川渡水2 小时前
NL2SQL 的正确打开方式:从「AI 猜 SQL」到「置信度闭环」的工程化落地
数据库·人工智能·sql
TDengine (老段)2 小时前
TDengine Go 与 Rust 连接器 — 高性能异步访问
大数据·数据库·物联网·golang·rust·时序数据库·tdengine
东方佑2 小时前
MA-RMSNorm:打破“缩放换智能”的魔咒,大模型归一化的一次范式革命!
数据库·人工智能·计算机视觉
云和恩墨3 小时前
「云帆计划·长沙站」活动成功举办,云和恩墨与紫薇垣和湖南金悦科技分别签约并授牌,本地伙伴生态建设加速
数据库·科技
千维百策6664 小时前
如何管理错误预算:提升服务可靠性与 SLO 达成率的实践经验
服务器·网络·数据库
Y3815326625 小时前
2026 年 7 月,SERP API 调用的稳定性实战:超时、重试、降级
开发语言·数据库·人工智能·php