Skip to content

05.3 - Binlog 详解

定位:Binlog 是 MySQL Server 层的日志,支撑了主从复制和数据恢复——理解它的三种格式及差异是部署高可用架构的基础 面试高频度:⭐⭐⭐⭐⭐ 考查方式:三种格式的区别及选型、Binlog 与 Redo Log 的区别、PITR(时间点恢复)、sync_binlog 配置


一、这是什么?为什么需要它?

是什么

Binlog(二进制日志)是 MySQL Server 层的日志,记录所有数据变更操作。与 InnoDB 引擎层的 Redo Log 不同,Binlog 对所有的存储引擎都适用。

为什么需要 Binlog?

Binlog 解决的是 Redo Log 无法覆盖的问题:

能力Redo LogBinlog
主从复制❌ 引擎层,无法跨实例✅ 核心能力
时间点恢复 (PITR)❌ 循环写,历史数据被覆盖✅ 追加写,可回溯到任意时间点
审计❌ 内容格式引擎私有✅ 可解析为 SQL 语句
跨引擎❌ 只有 InnoDB✅ 所有引擎

核心 insight:Redo Log 是"不丢数据"的保证,Binlog 是"数据可回溯"和"数据可复制"的保证。两者分工不同,缺一不可。


二、原理拆解

2.1 三种格式对比

STATEMENT(语句格式)

记录 SQL 语句本身。

binlog
# at 123456
#2024-01-15 10:30:00 server id 1
SET @@session.time_zone = '+08:00'/*!*/;
DELETE FROM users WHERE age > 30;
/*!*/;
特性说明
日志大小最小(只存 SQL 语句)
优点空间占用少,可读性强
缺点不安全——依赖上下文(时间函数、UUID、LOAD DATA INFILE 等)可能导致主从不一致
适用MySQL 5.7 之前默认,现已不推荐

ROW(行格式)

记录每一行数据的具体变更。

binlog
# at 789012
#2024-01-15 10:30:00 server id 1
### DELETE FROM `db`.`users`
### WHERE
###   @1=1    -- id=1
###   @2='Tom' -- name='Tom'
###   @3=25    -- age=25
特性说明
日志大小最大(每行变更全记录)
优点最安全——主从绝对一致
缺点空间占用大(尤其大范围 UPDATE/DELETE)
适用生产环境推荐(5.7+ 默认)

MIXED(混合格式)

STATEMENT + ROW 的自动切换。

sql
-- 大多数情况用 STATEMENT(省空间)
DELETE FROM users WHERE id = 1;  -- 用 STATEMENT

-- 遇到"不安全"语句自动切 ROW
DELETE FROM users WHERE create_time = NOW();  -- NOW() 不确定,切 ROW

DELETE FROM users LIMIT 10;  -- LIMIT 不确定,切 ROW
特性说明
日志大小中等
优点兼顾空间和安全
缺点自动切换规则复杂,可能出现意外不一致
适用少数场景,但不如直接 ROW

格式对比总结

对比维度STATEMENTROWMIXED
日志大小
一致性保证❌ 不安全✅ 绝对安全⚠️ 大部分安全
可读性✅ SQL❌ 难读看情况
恢复灵活度❌ 不支持闪回✅ 支持闪回看情况
性能影响大(尤其大事务)
推荐❌ 不推荐推荐⚠️ 少用

2.2 Binlog 写入机制

事务提交 → Binlog 写入流程:

1. 事务执行期间 → 产生的 Binlog 事件写入 Binlog Cache(每个事务独立)

2. 事务提交时 → Binlog Cache 写入 Binlog 文件
    └── sync_binlog 控制刷盘策略:
        ├── sync_binlog=1    → 每次提交刷盘 ✅ 最安全
        ├── sync_binlog=0    → 由 OS 决定刷盘(可能丢)
        └── sync_binlog=N    → 每 N 次提交刷一次盘

3. 事务提交成功 ✅

sync_binlog 配置对比:

安全性性能说明
1✅ 不丢数据每次提交 fsync,最安全
0❌ 可能丢最后 N 个事务依赖 OS 调度
N (如 100)⚠️ 最多丢 N 个事务批量刷盘,性能折中

生产推荐: sync_binlog = 1 + innodb_flush_log_at_trx_commit = 1

2.3 Binlog 的主从复制

              Binlog 复制流程

    Master                      Slave
┌──────────────┐         ┌──────────────┐
│   写入操作     │         │   SQL 线程    │
│       ↓       │         │       ↑       │
│   Binlog     │ ──────→ │   Relay Log  │
│   文件        │   IO    │   (中继日志)  │
└──────────────┘  线程   └───────┬──────┘


                           ┌──────────────┐
                           │   数据文件    │
                           └──────────────┘

步骤:
  1. Master 提交事务 → 写入 Binlog
  2. Slave IO 线程 → 从 Master 拉取 Binlog → 写入 Relay Log
  3. Slave SQL 线程 → 读取 Relay Log → 在 Slave 上重放

复制延迟的原因:

  • Slave SQL 线程是单线程的(直到 8.0 支持 MTS 多线程复制)
  • Master 的写入并发度高时,Slave 重放跟不上
  • 大事务(如大批量 DELETE)在 Slave 上执行时间长

2.4 时间点恢复(PITR)

bash
# 1. 全量备份
mysqldump --all-databases --single-transaction --master-data=2 > backup.sql

# 2. 恢复到某个时间点(比如误 DROP 之前)
mysql < backup.sql  # 先恢复全量

# 3. 从 Binlog 中找到并执行误操作之前的日志
mysqlbinlog --stop-datetime="2024-01-15 10:29:00" mysql-bin.000001 | mysql

# 4. 跳过误操作,继续执行后续
mysqlbinlog --start-datetime="2024-01-15 10:30:00" \
  --stop-datetime="2024-01-15 10:31:00" mysql-bin.000001 \
  | grep -v "DROP TABLE" | mysql

三、面试视角

追问答案要点
Binlog 和 Redo Log 的区别?Redo=引擎层物理日志(循环写),Binlog=Server 层逻辑日志(追加写)。Redo 保证持久性,Binlog 保证复制和恢复
三种 Binlog 格式怎么选?推荐 ROW——最安全,支持闪回。STATEMENT 不安全,MIXED 切换规则复杂
ROW 格式有什么缺点?日志量大(尤其大事务,ALTER TABLE 改结构全量记录),小心大事务导致磁盘满
sync_binlog=1 和 =0 的区别?1=每次事务提交刷盘(最安全),0=OS 控制(性能高但可能丢数据)
Binlog 怎么做数据恢复?全量备份 + Binlog 增量:mysqlbinlog 解析并重放到指定时间点
复制延迟怎么解决?配置 MTS(多线程复制,slave_parallel_workers)、避免大事务、使用并行复制

📚 相关链接

  • Redo Log:**Redo Log**
  • 两阶段提交:**两阶段提交与崩溃恢复**
  • 主从复制:**主从复制原理**
  • ← 返回 **日志系统索引**

Knowledge4J — Java 知识库