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(事务真正提交)
      ↓
释放锁 → 后台刷脏页
相关推荐
yngsqq2 小时前
兰顿蚂蚁——CAD二次开发
数据库
梁萌3 小时前
ShardingSphere分库分表实战
数据库·mysql·实战·shardingsphere·分库分表
川石课堂软件测试3 小时前
Mysql中触发器使用详详详详详解~
数据库·redis·功能测试·mysql·oracle·单元测试·自动化
鹏说大数据3 小时前
数据治理项目实战系列6-数据治理架构设计实战,流程 + 工具双架构拆解
大数据·数据库·架构
程序员游老板3 小时前
基于SpringBoot3_vue3_MybatisPlus_Mysql_Maven的社区养老系统/养老院管理系统
java·spring boot·mysql·毕业设计·软件工程·信息与通信·毕设
唯余旧忆3 小时前
【数据写入】达梦数据库(dm8)merge into写入时序数据速度慢的问题处理
数据库
小二·4 小时前
MyBatis基础入门《十四》多租户架构实战:基于 MyBatis 实现 SaaS 系统的动态数据隔离
数据库·架构·mybatis
白衣衬衫 两袖清风4 小时前
SQL联查案例
数据库·sql
ShirleyWang0124 小时前
VMware如何导入vmdk文件
linux·数据库