欢迎光临
我们一直在努力

UPDATE 背后发生了什么?MySQL 四大日志与两阶段提交全揭秘

文章目录

  • UPDATE 背后发生了什么?MySQL 四大日志与两阶段提交全揭秘
    • 一、先看全景——一条 UPDATE 的完整生命周期
    • 二、redo log——崩溃恢复的唯一保障
      • 它长什么样
      • 核心设计一:WAL(Write-Ahead Logging)
      • 核心设计二:固定大小的循环写
      • 核心设计三:刷盘策略
    • 三、undo log——回滚的逆操作 + MVCC 的版本链
      • 更重要的功能:撑起 MVCC 的版本链
    • 四、binlog——主从同步的唯一载体
      • 它长什么样
      • 和 redo log 的核心差异
      • 三种格式如何选
      • 主从同步怎么用 binlog
    • 五、relay log——从库的中转站
    • 六、两阶段提交——为什么不能只靠 redo log
      • 问题
      • 两阶段提交流程
      • 崩溃恢复逻辑
      • 生产环境最高安全配置
    • 七、DoubleWrite Buffer——redo log 也救不了的场景
      • 问题:部分页写
      • 解决方案
    • 八、Buffer Pool——脏页、干净页和数据的最终归宿
    • 九、四大日志一句话总结
    • 十、整个 UPDATE 流程图(串联所有知识点)
    • 总结

UPDATE 背后发生了什么?MySQL 四大日志与两阶段提交全揭秘

你写了一条 UPDATE,MySQL 返回"成功"。从 Buffer Pool 到磁盘,这中间经过了 undo log、redo log、binlog、两阶段提交。任何一个环节理解偏差,线上事故排查就多一分风险。


一、先看全景——一条 UPDATE 的完整生命周期

UPDATE t_user SET name = 'xiaolin' WHERE id = 1;

1. 执行器调用 InnoDB 接口,通过主键索引定位 id=1 的记录
→ 数据页在 Buffer Pool → 直接返回
→ 不在 → 从磁盘读到 Buffer Pool → 返回

2. 比对更新前后的值
→ 一样 → 跳过后续流程
→ 不一样 → 继续

3. 开启事务,写 undo log(记录旧值,用于回滚和 MVCC)
→ undo log 写入 Buffer Pool 的 Undo 页
→ Undo 页的修改也记 redo log(undo 也是数据,要持久化)

4. 修改 Buffer Pool 中的数据页(标记为脏页)

5. 将这次修改记录到 redo log buffer(内存中)

6. 写 binlog cache(内存中)

此时事务还没提交。提交的那一刻,两阶段启动:

7. PREPARE 阶段:redo log buffer 刷盘(fsync),事务状态=prepare

8. COMMIT 阶段:
→ binlog cache 刷盘到 binlog 文件
→ redo log 标记为 commit(并刷盘)
→ 返回"提交成功"给客户端

9. 脏页排队,后台线程异步刷回磁盘数据文件

每一步都有原因。下面四篇日志逐一拆。


二、redo log——崩溃恢复的唯一保障

它长什么样

ib_logfile0, ib_logfile1(磁盘文件,固定大小,循环写)

[LSN 1000: 页号=5, offset=200, 改 2 字节为 0x1A2B]
[LSN 1001: 页号=5, offset=300, 改 2 字节为 0x3C4D]

→ 物理日志:哪个页的哪个位置,改成什么

核心设计一:WAL(Write-Ahead Logging)

不直接改磁盘数据——太慢。先顺序写 redo log,再内存改 Buffer Pool,异步刷脏页。

随机刷脏页(没有 redo log):
页3 在偏移 500MB → 页17 在偏移 200MB → 页42 在偏移 1GB
→ 磁盘头满盘寻道 → 巨慢

顺序写 redo log(WAL):
→ 磁盘头一直往下写 → 快几十倍 → 写完就返回

核心设计二:固定大小的循环写

┌─────────────────────────────────┐
│ 已刷盘可覆盖 │ 未刷盘保护中 │ 空闲 │
└─────────────────────────────────┘
↑ ↑ ↑
checkpoint write pos

write pos 追上 checkpoint → redo log 满了 → 所有写入暂停 → 强制刷脏页推进 checkpoint

redo log 不能无限保留——它只保护"已修改但未刷盘的脏页"。脏页一刷盘,对应 redo log 就可覆盖。

核心设计三:刷盘策略

innodb_flush_log_at_trx_commit = 1 — 默认,每次提交都 fsync,最安全
= 0 — 每秒刷一次,可能丢 1 秒数据
= 2 — 写 OS 缓存,机器宕机丢数据

三、undo log——回滚的逆操作 + MVCC 的版本链

事务里改了数据,但还没提交。随时可能回滚——undo log 记录的就是逆操作:

UPDATE user SET age = 30 WHERE id = 1;
→ undo log 记录:把 id=1 的 age 改回 25(旧值)

回滚时按 undo log 反着执行一次,数据回到老状态。

更重要的功能:撑起 MVCC 的版本链

每行数据有三个隐藏列:

隐藏列6/7/6 字节作用
DB_TRX_ID 6 最近修改这行的事务 ID
DB_ROLL_PTR 7 指向 undo log 旧版本的指针
DB_ROW_ID 6 没主键时的兜底行 ID

同一行多次修改,通过 DB_ROLL_PTR 串成一条版本链:

当前版本 trx_id=100, age=35
↑ DB_ROLL_PTR
旧版本 trx_id=80, age=30
↑ DB_ROLL_PTR
旧版本 trx_id=50, age=25

ReadView(快照)在这条链上帮你挑一个能看的版本。这个过程就是 MVCC。

注意:undo log 也写 redo log——undo 页变更要先记 redo log,防止 undo 本身丢了。


四、binlog——主从同步的唯一载体

它长什么样

mysql-bin.000001(磁盘文件,追加写,不覆盖,满了切新文件)

STATEMENT 格式:
UPDATE t_user SET name = 'xiaolin' WHERE id = 1;

ROW 格式(推荐):
### UPDATE `db`.`t_user`
### WHERE id=1
### SET name='old_name' → name='xiaolin'

→ 逻辑日志:记录 SQL 语句或每行变更前后值。

和 redo log 的核心差异

redo logbinlog
层级 InnoDB 引擎层 Server 层
类型 物理日志 逻辑日志
写入方式 循环写(固定大小) 追加写(不覆盖)
作用 崩溃恢复 主从复制 + 数据恢复
能否关闭 不能 能(–skip-binlog)

三种格式如何选

STATEMENT:记 SQL 语句,省空间。
坑:NOW() / UUID() 在主从执行结果不同 → 不一致

ROW:记每行变更的值,安全。
代价:改 1 万行 = 1 万个事件,binlog 巨大

MIXED:默认 STATEMENT,遇到非确定性语句自动切 ROW。
坑:切换边界有时不是你预期的那样

线上建议:直接 ROW。磁盘不值钱,数据不一致才是事故。

主从同步怎么用 binlog

主库:执行 UPDATE → 写 binlog
从库:IO 线程拉 binlog → 写 relay log → SQL 线程回放 relay log

从库同步位置靠 binlog 的位点(offset)记录。断连重连后,从上次位点继续追——因为 binlog 是追加写、不覆盖,断多久都能续上。


五、relay log——从库的中转站

从库上:

IO 线程:从主库拉 binlog → 写入 relay log
SQL 线程:读取 relay log → 执行 SQL → 删除 relay log

格式跟 binlog 完全一样,就是个临时的中继日志。回放完一条清一条。


六、两阶段提交——为什么不能只靠 redo log

问题

如果 redo log 和 binlog 各自提交:

场景1:redo 成功,binlog 失败
→ 主库恢复后数据在,从库没有 → 主从不一致

场景2:binlog 成功,redo 失败
→ 从库有数据,主库没有 → 主从不一致

两阶段提交流程

1. PREPARE:redo log 刷盘,事务状态=prepare
2. 写 binlog 并刷盘
3. COMMIT:redo log 标记=commit,刷盘

崩溃恢复逻辑

重启 → 扫描 redo log → 找所有 PREPARE 状态的事务
→ 查 binlog:
有完整记录 → 提交(补上 redo log 的 commit)
没有记录 → 回滚

核心原则:binlog 说了算。 从库同步全靠 binlog——binlog 有的事务主库必须留,binlog 没有的事务主库必须删。

生产环境最高安全配置

innodb_flush_log_at_trx_commit = 1 — redo log 每次提交 fsync
sync_binlog = 1 — binlog 每次提交 fsync
binlog_format = ROW — 行级复制最安全


七、DoubleWrite Buffer——redo log 也救不了的场景

问题:部分页写

一页 16KB,OS 刷盘最小单位 4KB。写入中途断电:

16KB 页 = 4 个 OS 块

块1 ✓ 写完
块2 ✗ 没写
块3 ✓ 写完
块4 ✗ 没写

→ 磁盘上的页 = 新旧数据混在一起 → checksum 不匹配 → 坏页

这个坏页 redo log 救不回来——redo log 记录的是"在完整页上改什么",页碎了连地基都没了,指令无处施加。

解决方案

脏页刷盘分两步:

Step 1:顺序写到 DoubleWrite Buffer(磁盘上 2MB 连续区域)
Step 2:随机写到数据文件的实际位置

崩溃恢复:
检查数据页 → checksum 通过 → 正常恢复
→ checksum 失败 → 从 DWB 拿完整副本替补
→ 再用 redo log 追到最新版本

redo log 保"数据是新是旧",DWB 保"页本身没碎"。 缺一个都不行。


八、Buffer Pool——脏页、干净页和数据的最终归宿

Buffer Pool 是内存缓存,两类页并存:

干净页:跟磁盘一致 → 崩了就崩了,重启重读磁盘
脏页: 修改过未刷盘 → 崩了靠 redo log 恢复

脏页是数据写入磁盘的唯一入口——任何数据要持久化,必须先在 Buffer Pool 里待过:

客户端 UPDATE → Buffer Pool(脏页)→ redo log(保险)→ 后台刷盘 → 持久化
↑ 改了马上能查 ↑ 时机由后台控制

脏页比例太高 → Buffer Pool 塞满脏页 → 新查询没空间加载数据页 → 必须等脏页先刷盘 → 整库卡顿。

redo log 满了也一样——必须等脏页刷盘才能推进 checkpoint,释放 redo log 空间,期间所有写入暂停。


九、四大日志一句话总结

日志在哪形态作用写满咋办
redo log 磁盘 ib_logfile 物理日志 崩溃恢复 等刷脏页释放
undo log undo 表空间 逻辑逆操作 回滚 + MVCC purge 线程清理
binlog 磁盘 binlog 文件 逻辑 SQL/行变更 主从同步 切新文件
relay log 从库磁盘 同 binlog 从库中转 回放完自动删

redo 管崩溃,bin 管复制,undo 管回滚,两阶段管一致性。


十、整个 UPDATE 流程图(串联所有知识点)

客户端发起 UPDATE

执行器 → InnoDB 通过聚簇索引定位 id=1 的数据页

Buffer Pool 有这页?──N──→ 从磁盘读取 16KB 页到 Buffer Pool
↓ Y
执行器比对更新前后值是否相同
↓ 不同
写 undo log(记录旧值,存到 Buffer Pool 的 Undo 页)

undo log 变更记 redo log buffer(undo 页也是数据)

更新 Buffer Pool 中的数据页(标记为脏页)

将修改记录写入 redo log buffer(内存)

将 binlog 记录写入 binlog cache(内存)

══════ 事务 COMMIT ══════

PREPARE:redo log buffer → fsync 到磁盘 ib_logfile(事务状态=prepare)

写 binlog:binlog cache → fsync 到磁盘 binlog 文件

COMMIT:redo log 标记=commit,fsync 到磁盘

返回客户端 "提交成功"

后台线程异步将脏页从 Buffer Pool 刷到磁盘数据文件
(DoubleWrite Buffer 保护,防止部分页写)


总结

  • redo log = 崩溃恢复:WAL 把随机 IO 变顺序 IO,固定大小循环写,脏页刷盘后可覆盖
  • undo log = 回滚 + MVCC:记录逆操作,DB_ROLL_PTR 串成版本链,ReadView 判断可见性
  • binlog = 主从同步:追加写不覆盖,ROW 格式保一致,从库 IO 线程拉 + SQL 线程放
  • 两阶段提交 = binlog 是老大:崩溃恢复时扫描 prepare 事务,binlog 有的提交、没有的回滚
  • DoubleWrite Buffer = 防部分页写:脏页刷盘前先写 DWB 做副本,页碎了能从副本还原再追 redo log
  • redo 管崩溃,bin 管复制,undo 管回滚,DWB 管页碎,2PC 管一致。Buffer Pool 只管快,脏页刷盘后台来。

    如果觉得有用,点赞收藏。


    赞(0)
    未经允许不得转载:171主机测评 » UPDATE 背后发生了什么?MySQL 四大日志与两阶段提交全揭秘
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址