MySQL日志系统 (Redo Log 与 Binlog)
# MySQL 日志系统
与查询流程不同,更新流程涉及两个重要的日志模块:redo log(重做日志) 和 binlog(归档日志)。
# 1. Redo Log (重做日志)
- 作用: 保证事务的持久性 (Durability)。是 InnoDB 存储引擎特有的日志。
- WAL 技术: MySQL 通过 Write-Ahead Logging (WAL) 技术,在执行更新操作时,先将修改记录写入 redo log(此时是顺序写,性能较高),并更新内存中的数据页,然后就认为更新完成了。InnoDB 会在系统比较空闲的时候,将这批操作实际更新到磁盘的数据文件里(此时是随机写)。
- Crash-Safe: 有了 redo log,即使数据库发生异常重启,之前已提交事务的记录也不会丢失。InnoDB 在重启后会通过 redo log 恢复数据,这个能力称为 crash-safe。
- 物理日志: Redo log 记录的是物理修改,即“在某个数据页上做了什么修改”。
- 循环写: Redo log 文件组是固定大小、循环写入的。当写到末尾时会回到开头覆盖写。如果 redo log 写满,必须先将部分日志对应的脏页刷到磁盘 (checkpoint),才能继续写入新的日志。
# 2. Binlog (归档日志)
- 作用: 主要用于数据恢复(结合备份)和主从复制。是 MySQL Server 层实现的日志,所有存储引擎都可以使用。
- 逻辑日志: Binlog 记录的是逻辑操作,即 SQL 语句的原始逻辑。
- Statement 格式: 记录原始 SQL 语句。可能导致主从不一致。
- Row 格式: (推荐) 记录行的变更内容(更新前和更新后的镜像)。能保证主从一致性,但日志量较大。
- Mixed 格式: 混合模式,MySQL 自行判断使用 Statement 或 Row 格式。
- 追加写: Binlog 是可以追加写入的。当一个文件写到一定大小(由
max_binlog_size
控制)或遇到特定事件时,会切换到下一个日志文件,不会覆盖之前的日志。
# 3. Redo Log 与 Binlog 的区别
特性 | Redo Log | Binlog |
---|---|---|
实现层 | InnoDB 存储引擎层 | MySQL Server 层 |
内容 | 物理日志(数据页修改) | 逻辑日志(SQL 或行变更) |
写入方式 | 循环写,空间固定 | 追加写,空间可增长 |
用途 | Crash-Safe 恢复 | 备份恢复、主从复制 |
引擎特定 | InnoDB 特有 | 所有引擎可用 |
# 4. 更新语句执行流程与两阶段提交
一个 UPDATE
语句的内部执行流程如下:
- 执行器先找引擎取 ID=N 这一行。如果数据页在内存中直接返回;否则,从磁盘读入内存再返回。
- 执行器拿到行数据后,把值加上 1,得到新的一行数据。
- 执行器调用引擎接口,写入这行新数据。
- 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
- 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
- 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成 commit 状态,更新完成。
两阶段提交 (Two-Phase Commit, 2PC):
- 将 redo log 的写入拆分为
prepare
和commit
两个阶段,目的是为了让 redo log 和 binlog 这两份日志之间的逻辑保持一致。 - 重要性: 如果没有两阶段提交,在写入 redo log 后、写入 binlog 前发生崩溃,重启后 redo log 会恢复数据,但 binlog 没有记录,导致备份恢复或从库同步时丢失数据。如果在写入 binlog 后、redo log commit 前崩溃,binlog 会记录操作,但 redo log 未 commit,重启后数据未恢复,导致备份恢复或从库同步时多出数据。
- 两阶段提交保证了事务在存储引擎层面(redo log)和 Server 层面(binlog)要么都成功,要么都失败,从而保证了数据的一致性。
# 5. 基于 Binlog 的数据恢复
误删数据后的恢复过程(例如,恢复到误删前的状态):
- 找到最近的一次全量备份,将其恢复到一个临时库。
- 从备份的时间点开始,取出之后产生的 binlog 文件。
- 使用
mysqlbinlog
工具,将这些 binlog 重放到临时库中,直到误删操作之前的那个时间点或 GTID。 - 此时临时库的数据就是误删前的状态,可以将需要的表数据从临时库导出,再导入到线上库。
上次更新: 6/21/2025