通过一条 UPDATE
语句,搞懂mysql的幕后
引言
你可能每天都在写 SQL,但是否真正了解,一条 UPDATE
语句在 MySQL 中经历了哪些阶段,哪些日志在为你的事务保驾护航,哪些机制在悄悄保障着数据一致性?
今天我们通过一个简单的更新操作,带你从执行器到磁盘,深入理解 MySQL 的底层机制。
0. SQL 语句
sql
UPDATE user SET age = 16 WHERE id = 1;
目标很明确:把 user
表中 id = 1
的那行记录的 age
字段更新为 16
。
1. SQL 进入 MySQL 后,发生了什么?
第一步:连接层处理
- 客户端发来 SQL,进入 连接层,MySQL 验证权限并分配线程。
第二步:SQL 层解析与优化
- 解析器将 SQL 字符串转换为语法树。
- 预处理器校验表、字段是否存在。
- 优化器选择最优的执行路径(一般是走主键索引)。
- 执行器准备执行具体操作。
2. 执行器找存储引擎"办事":InnoDB 来了
MySQL 使用 InnoDB 存储引擎时,数据存储、事务和日志控制都在它手上完成。
3. 开始执行:InnoDB 的详细执行流程
下面我们进入"时间线"视角,来看 InnoDB 是如何一步步处理这条语句的:
🧭 3.1 查询数据页
- 根据主键
id = 1
,从 Buffer Pool(缓冲池) 查找对应的数据页。 - 如果命中,直接操作;否则从磁盘读取页,加载到 Buffer Pool。
✅ Buffer Pool 是内存中的数据页缓存,避免频繁磁盘读写。
🪓 3.2 生成 undo log(撤销日志)
- 假设原来该行的
age = 18
。 - InnoDB 会先生成一条 undo log:
text
undo log: update user set age = 18 where id = 1;
- 写入 undo log buffer ,后续会刷入磁盘的 undo tablespace。
- 用于事务回滚或 MVCC 快照读。
🔧 3.3 修改 Buffer Pool 中的数据页
- 将 age 字段更新为 16,但只是修改内存数据页,并未写入磁盘!
🔁 3.4 生成 redo log(重做日志)
- 为了防止崩溃丢失,写入一条物理层的变更日志,记录:
text
redo log: 页号 XX 中的 offset XX 的 age 字段值从 18 → 16
- Redo log 被写入 redo log buffer,准备刷盘。
4. 事务日志提交:两阶段提交登场
InnoDB 的核心保障:原子性和崩溃恢复,靠 redo log 和 binlog 联手完成。
🚩 4.1 prepare 阶段
- 将 redo log 刷入磁盘(
ib_logfile
文件),状态为 prepare。
✍️ 4.2 写入 binlog(二进制日志)
- MySQL Server 层生成 binlog(逻辑 SQL):
text
binlog: update user set age = 16 where id = 1;
- 写入 binlog buffer,刷入磁盘。
✅ 4.3 commit 阶段
- InnoDB 标记 redo log 状态为 commit。
- 事务正式提交,客户端收到"更新成功"响应。
❗如果在 commit 前宕机,可以用 redo log 恢复;如果写 binlog 后崩溃但 redo log 未 commit,MySQL 会回滚事务,保持一致性。
5. 三种日志作用对比
日志类型 | 记录内容 | 写入时机 | 应用场景 | 属于哪一层 |
---|---|---|---|---|
undo log | 数据修改前的旧值 | 修改数据之前 | 回滚、MVCC 快照读 | InnoDB |
redo log | 数据页的物理变更 | 修改之后(prepare) | 崩溃恢复 | InnoDB |
binlog | SQL 的逻辑变更 | 提交事务时 | 主从复制、增量备份 | Server 层 |
6. 多操作事务如何保证原子性?
假设你有如下事务:
sql
START TRANSACTION;
UPDATE user SET age = 16 WHERE id = 1;
UPDATE user SET name = 'Tom' WHERE id = 1;
COMMIT;
- 所有修改都在同一个事务 ID 下。
- 每条语句都生成自己的 undo log、redo log。
- 在
COMMIT
时,统一写入 binlog 和 redo commit。 - 任何一个操作失败或中断,全部回滚 ------ 原子性保障!
7. 整体流程图(逻辑顺序)
perl
客户端
↓
SQL 解析、优化
↓
执行器调用 InnoDB
↓
查 Buffer Pool(命中 or 加载磁盘页)
↓
生成 undo log
↓
更新 Buffer Pool 中数据
↓
写 redo log(prepare)
↓
写 binlog
↓
redo log commit → 事务提交
详细图如下:
8. 结语
虽然只是简单更新一个字段,但背后 MySQL 做了大量精密工作------从内存控制、日志写入到事务保障,处处体现出设计者的匠心。
最后
如果文章对你有帮助,点个免费的赞鼓励一下吧!关注gzh:加瓦点灯, 每天推送干货知识!