今日关键词Redo Log、Binlog、Undo Log、WAL、两阶段提交、crash-safe大家好我是数据库小学妹之前我们学过 MVCC知道 Undo Log 可以实现数据的时光回溯。但很多同学问“MySQL到底有几种日志它们各自管什么事为什么要有这么多日志而不是一个搞定”说实话刚开始我也一头雾水——明明都是记录变更为什么要搞 Redo Log、Binlog、Undo Log 三个概念直到我真正理解了它们的分工才发现这是数据库设计中最精妙的部分之一。今天用一条 UPDATE 语句走一遍流程把这三个东西的关系理清楚。一、三种日志分工日志类型归属作用删除策略Redo LogInnoDB 引擎崩溃恢复crash-safe循环覆盖Undo LogInnoDB 引擎事务回滚 MVCC事务结束后可被 purgeBinlogServer 层主从复制 数据恢复手动清理或按策略归档记住一句话Redo Log 负责死了能活Undo Log 负责错了能悔Binlog 负责丢了能找。二、Redo Log —— 宕机后靠它恢复数据什么是 Redo Log想象一下这个场景你刚给某个用户充值了 100 元UPDATE 语句执行到一半服务器突然断电了。数据会怎样如果不做任何处理这笔账就丢了——因为内存里的修改还没来得及刷到磁盘上。Redo Log 的作用就是即使宕机已提交的事务也不会丢失。Redo Log 的核心特点1. 环形文件结构redo log000001 → redo log000002 → ... → redo log000010 → (回到开头) redo log000001Redo Log 是固定大小的环形文件组默认总共 1GB写到末尾就从头开始覆盖。这种设计保证了日志不会因为长期运行而撑爆磁盘。2. 物理日志Redo Log 记录的是在某个数据页上做了什么修改比如[LSN100] 在 page_id100 的 offset4096 处写入 value100 元这种物理级别的记录让恢复速度非常快——直接按日志重放就行不用重新解析 SQL。3. WAL 技术Write-Ahead Logging核心原则任何修改必须先写 Redo Log才能认为事务成功。正常流程有风险 修改内存 buffer pool → 异步刷盘 → 告诉用户成功了 ↑ 如果这里宕机数据就丢了 WAL 流程安全 写 Redo Log 并刷盘 → 修改内存 buffer pool → 告诉用户成功了 ↑ 即使宕机重启后通过 Redo Log 能恢复查看 Redo Log 配置-- 查看 Redo Log 大小和数量SHOWVARIABLESLIKEinnodb_log_file_size;-- 单个文件 256MSHOWVARIABLESLIKEinnodb_log_files_in_group;-- 4 个文件共 1GB-- 查看 checkpoint 位置决定了能回收多少 logSHOWENGINEINNODBSTATUS\G三、Undo Log —— 回滚和版本链Undo Log 我们之前学 MVCC 时提过今天从更底层角度再拆解一遍。Undo Log 的两个职责职责 1事务回滚你执行了一个错误操作比如BEGIN;UPDATEaccountsSETbalance0WHEREuser_id123;-- 哎呀手滑了ROLLBACK;数据库怎么知道你原来的余额是多少靠的就是 Undo Log 里保存的旧值。Undo Log 记录的格式类似这样[事务 A] 将 accounts 表中 user_id123 的 balance 从 500 改为 0 ↓ 反向操作就是 [回滚] 将 accounts 表中 user_id123 的 balance 从 0 改回 500职责 2MVCC 版本链同一个表的同一行数据不同事务看到的版本可能不一样时间线 T1: 事务 A 把 balance 从 100 改成 200undo log 指向旧版本 T2: 事务 B 把 balance 从 200 改成 300undo log 指向 T1 的版本 T3: 事务 C 查询如果是 RC 级别看到 300如果是 RR 级别且早于 T1 则看到 100每一行数据的 undo log 连成一条版本链越新的数据在链表头部越旧的在后面。Undo Log vs Redo Log特性Undo LogRedo Log存储位置表空间内系统表空间或独立 undo 表空间独立的 redo log 文件是否循环使用否事务结束后被 purge 线程清理是checkpoint 后可覆盖记录内容逻辑日志反向日志物理日志数据页修改主要用途回滚 MVCC崩溃恢复四、Binlog —— Server 层的日志文件Binlog 是什么BinlogBinary Log是 MySQL Server 层的日志所有引擎InnoDB、MyISAM 等都会产生。它记录的是 SQL 语句的逻辑变更比如# At 123456789 #260521 10:30:00 server id 1 end_log_pos 123456789 Query SET timestamp1684658400; UPDATE accounts SET balance 100 WHERE id 1Binlog 的三种格式-- 查看当前 binlog 格式SHOWVARIABLESLIKEbinlog_format;格式记录内容优点缺点STATEMENT原始 SQL 语句体积小某些函数如 NOW()主从不一致ROW每行变更的前后镜像精确可靠DDL 或批量更新时体积大MIXED混合模式自动切换平衡两者逻辑复杂生产环境推荐用 ROW 模式虽然体积大点但最安全。Binlog 的独特价值1. 数据恢复Point-in-Time Recovery误删了一张表可以通过 Binlog 恢复到任意时间点# 停止 MySQL# 恢复全量备份# 回放 Binlog 到删除前的时间点mysqlbinlog --stop-datetime2026-05-21 10:29:59binlog.000001|mysql-uroot-p2. 主从复制从库通过读取主库的 Binlog保持数据同步——这是我们之前学主从复制时的基础。Binlog 管理命令-- 查看 Binlog 列表SHOWBINARYLOGS;-- 清空过期 Binlog谨慎操作PURGEBINARYLOGS BEFORE2026-05-01 00:00:00;-- 强制刷新 Binlog 到新文件FLUSH LOGS;-- 关闭/开启 Binlog一般不用SETGLOBALlog_bin0;五、一条 UPDATE 语句的执行流程现在看执行一条 UPDATE 时三个日志怎么协作UPDATEaccountsSETbalancebalance100WHEREuser_id1;执行流程┌─────────────────────────────────────────────────────────────┐ │ 1. 检查 Buffer Pool找到对应的数据页 │ │ ↓ │ │ 2. 生成 Undo Log记录旧值 balance500 │ │ ↓ │ │ 3. 修改内存中的数据页balance 变成 600 │ │ ↓ │ │ 4. 写 Redo Log记录page X 的 offset Y 修改为 600 │ │ ↓ │ │ 5. 写 Binlog记录UPDATE accounts... │ │ ↓ │ │ 6. 两阶段提交先准备prepare再提交commit │ │ ↓ │ │ 7. 返回给客户端Query OK, 1 row affected │ └─────────────────────────────────────────────────────────────┘关键点两阶段提交为什么不写完 Redo Log 和 Binlog 就直接提交而要分两个阶段场景假设写完 Redo Log 并提交然后宕机结果Redo Log 里有这条记录但 Binlog 没有后果主库恢复了但从库没有这条变更记录→数据不一致两阶段提交的解决方案阶段 1Prepare 写 Redo Log标记为prepare状态 ↓ 阶段 2Commit 写 Binlog 并刷盘 写 Redo Log 的 commit 标记 ↓ 事务完成如果中途宕机重启后通过检查 Redo Log 的状态来决定是否恢复只有 prepare 标记 → 不恢复说明 Binlog 没写完有 commit 标记 → 恢复六、实战监控与调优1. 检查 Redo Log 使用情况-- 查看 checkpoint 进度SHOWENGINEINNODBSTATUS\G# 搜索 LOG 部分关注# - Log sequence number当前 LSN# - Checkpoint age距离 checkpoint 还有多远Checkpoint age 过大说明刷盘跟不上写入速度可以考虑增大 innodb_log_file_size优化写入负载2. Undo Log 膨胀排查长事务会导致 Undo Log 无法被清理-- 查找长事务SELECTtrx.trx_id,trx.trx_state,trx.trx_started,TIMESTAMPDIFF(SECOND,trx.trx_started,NOW())asrunning_secondsFROMinformation_schema.innodb_trx trxWHERETIMESTAMPDIFF(SECOND,trx.trx_started,NOW())60;发现有长事务要尽快定位原因通常是因为业务代码里事务包裹的范围太大。3. Binlog 大小监控-- 查看所有 binlog 总大小SELECTFILE_NAMEaslog_name,FILE_LENGTHassize_bytes,ROUND(FILE_LENGTH/1024/1024,2)assize_mbFROMinformation_schema.FILESWHEREFILE_TYPEREDO LOGORFILE_NAMELIKE%binlog%;-- 或者直接从系统表查SHOWBINARYLOGS;Binlog 占用太大时可以调整保留策略# my.cnf [mysqld] expire_logs_days 7 # 7 天后自动删除 max_binlog_size 100M # 单个 binlog 最大 100M七、常见问题速答Q1: Redo Log 和 Binlog 的区别A: Redo Log 是 InnoDB 引擎层的物理日志用于崩溃恢复循环使用Binlog 是 Server 层的逻辑日志用于主从复制和数据恢复不会自动覆盖。Q2: 为什么要两阶段提交A: 保证 Redo Log 和 Binlog 的一致性。如果一个事务在 Redo Log 中提交了但 Binlog 没写完单阶段提交会导致主从数据不一致。Q3: Undo Log 什么时候被清除A: 当事务不需要再读旧版本时Purge 线程会在后台清理 Undo Log。具体来说RR 级别下事务结束后可清理RC 级别下每条 SELECT 后可清理不再需要的旧版本。Q4: Redo Log 满了怎么办A: Redo Log 是环形文件满了就从头开始覆盖。但有个前提_checkpoint_必须已经追赶上来即对应的脏页已经刷回磁盘。如果写入速度太快导致 checkpoint 追不上MySQL 会挂起写入直到有空间。八、今日学习心得Redo Log 是 InnoDB 的保险箱确保宕机后数据不丢失Undo Log 是事务的后悔药同时支撑 MVCC 的版本链Binlog 是 Server 层的审计日志用于主从复制和时间点恢复两阶段提交是保证 Redo Log 和 Binlog 一致性的关键理解这三种日志才能真正掌握 MySQL 的事务机制 我是数据库小学妹一个用设计师思维学数据库的转行人。我们一起把复杂的技术变得简单有趣本文示例基于MySQL8.0 InnoDB。