MySQL Binlog 深度详解
Binlog 是 MySQL 最重要的基础设施之一,理解 binlog 是掌握 MySQL 主从复制、CDC 数据同步、数据恢复的必经之路。本文从原理、结构、格式、复制流程、配置参数五个维度全面解析 binlog。
一、Binlog 是什么
Binlog(Binary Log,二进制日志) 是 MySQL Server 层记录的日志文件,记录了所有修改数据(INSERT / UPDATE / DELETE)的原始语句或行变更内容,并按照时间顺序追加写入。
核心用途
| 用途 | 说明 |
|---|---|
| 主从复制 | 从库读取主库的 binlog,应用这些变更,达到主从数据一致 |
| 增量备份 | 配合全量备份,实现任意时间点(PITR)的数据恢复 |
| 变更数据捕获(CDC) | Debezium / Canal 等工具伪装从库,读取 binlog 事件,实时同步到其他系统 |
| 审计追踪 | 记录所有数据变更操作,可用于安全审计 |
Binlog 与其他日志的区别
| 日志类型 | 记录内容 | 是否物理日志 | 用途 |
|---|---|---|---|
| Binlog | 数据变更(逻辑) | 逻辑日志 | 主从复制、CDC、增量备份 |
| Redo Log | 物理页变更 | 物理日志 | 崩溃恢复(InnoDB) |
| Undo Log | 行记录的旧版本 | 逻辑日志 | MVCC 事务回滚 |
| Slow Query Log | 慢查询 SQL | - | 性能分析 |
| Error Log | 启动/错误信息 | - | 故障排查 |
⚠️ Binlog 是 MySQL Server 层的日志,与存储引擎无关(MyISAM 和 InnoDB 都支持),而 Redo Log / Undo Log 是 InnoDB 引擎层的物理日志。
二、Binlog 的物理结构
2.1 文件组织
mysql-bin.000001 ← 当前正在写入的 binlog 文件
mysql-bin.000002
mysql-bin.000003
mysql-bin.index ← 索引文件,记录当前有哪些 binlog 文件
| 文件 | 说明 |
|---|---|
mysql-bin.000001 |
binlog 数据文件(后缀数字递增) |
mysql-bin.index |
文本文件,记录所有 binlog 文件的绝对路径,用于主从复制时从库请求 |
2.2 单个 binlog 文件内部结构
┌─────────────────────────────────────────────────────┐
│ Binlog File │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Event 1 │ │ Event 2 │ │ Event 3 │ ... │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Fixed Header (19 bytes) │ │
│ │ ├── timestamp (4B) │ │
│ │ ├── event type (1B) │ │
│ │ ├── server_id (4B) │ │
│ │ ├── event_length (4B) │ │
│ │ ├── next_position (4B) │ │
│ │ └── flags (2B) │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
每个事件(Event)由两部分组成:
| 部分 | 大小 | 内容 |
|---|---|---|
| Header | 固定 19 字节 | 时间戳、事件类型、server_id、事件长度、下一事件位置、标志位 |
| Body | 变长 | 具体的事件内容(根据类型不同,内容差异很大) |
三、Binlog 事件类型详解
3.1 事件分类总览
Binlog Events
├── FORMAT_DESCRIPTION_EVENT ← 文件头,描述binlog版本及event类型列表
├── ROTATE_EVENT ← 切换到下一个binlog文件
├── TABLE_MAP_EVENT ← 记录当前操作的表结构(仅ROW模式)
├── WRITE_ROWS_EVENT ← INSERT行写入(仅ROW模式)
├── UPDATE_ROWS_EVENT ← UPDATE行变更(仅ROW模式)
├── DELETE_ROWS_EVENT ← DELETE行删除(仅ROW模式)
├── QUERY_EVENT ← SQL语句(STATEMENT模式)
├── BEGIN / COMMIT ← 事务边界
├── GTID_LOG_EVENT ← 全局事务ID(GTID模式)
├── XID_EVENT ← 事务提交标记
├── DDL_EVENT ← 表结构变更(CREATE/ALTER/DROP)
└── INCIDENT_EVENT ← 异常事件
3.2 核心事件详解
FORMAT_DESCRIPTION_EVENT(文件头)
binlog 文件打开时写入的第一个事件,记录 MySQL 版本信息、binlog 版本、事件类型列表。
TABLE_MAP_EVENT(仅 ROW 模式)
在 ROW 格式下,每次操作表之前,先记录一个 TABLE_MAP_EVENT,说明接下来要操作哪个表的哪个库:
{
"database": "ecommerce",
"table": "products",
"columns": ["id", "name", "price", "stock"],
"columnTypes": [3, 253, 246, 3] // MySQL字段类型枚举值
}
WRITE_ROWS_EVENT / UPDATE_ROWS_EVENT / DELETE_ROWS_EVENT(仅 ROW 模式)
| 事件 | 对应操作 | 记录内容 |
|---|---|---|
WRITE_ROWS_EVENT |
INSERT | 插入的完整行数据 |
UPDATE_ROWS_EVENT |
UPDATE | 变更前的值 + 变更后的值 |
DELETE_ROWS_EVENT |
DELETE | 被删除的行数据 |
以 UPDATE 为例,ROW 格式记录的完整信息:
# UPDATE products SET price=2999, stock=50 WHERE id=1
# BEFORE(变更前):
{"id": 1, "name": "手机", "price": 1999, "stock": 100}
# AFTER(变更后):
{"id": 1, "name": "手机", "price": 2999, "stock": 50}
QUERY_EVENT(STATEMENT 模式)
在 STATEMENT 格式下,记录原始 SQL 语句:
UPDATE products SET price=2999, stock=50 WHERE id=1;
GTID_LOG_EVENT(GTID 模式)
MySQL 5.6+ 引入的全局事务 ID,每个事务分配一个唯一的 GTID:
格式:{source_id}:{transaction_id}
示例:uuid:1-99-123 → server_uuid=uuid,第99台服务器,第123个事务
四、三种 Binlog 格式
这是 CDC 场景最重要的配置点。
4.1 STATEMENT 格式(基于语句)
记录内容:执行的 SQL 语句原文
UPDATE products SET price = 2999 WHERE id = 1;
优点:
- 日志体积小(只记 SQL,不记每行数据)
- 可以回放,审计时可看到原始 SQL
缺点:
- 某些函数执行结果不确定(
NOW()、RAND()、UUID()),从库执行结果与主库不一致 - 无法可靠复制 DELETE 中的 WHERE 条件
- CDC 工具无法使用(无法还原每行变更)
4.2 ROW 格式(基于行)【CDC 必须使用】
记录内容:每行数据变更前后的完整值
// UPDATE products SET price=2999 WHERE id=1
{
"before": {"id": 1, "price": 1999},
"after": {"id": 1, "price": 2999}
}
优点:
- 任何变更都可以精确还原,CDC 工具依赖此格式
- 不受函数不确定性影响
- 主从复制 100% 一致
缺点:
- 日志体积大(每行变更都记录完整数据)
-大事务风险:修改 100 万行数据,binlog 体积可能膨胀数 GB
4.3 MIXED 格式(混用)
MySQL 自动选择:安全可确定的语句用 STATEMENT,不安全的用 ROW。
示例:
UPDATE products SET updated_at=NOW() WHERE id=1; → ROW(NOW()不确定)
UPDATE products SET price=2999 WHERE id=1; → STATEMENT(确定)
4.4 三种格式对比
| 维度 | STATEMENT | ROW | MIXED |
|---|---|---|---|
| 日志体积 | 小 | 大 | 中 |
| CDC 兼容性 | ❌ 不可用 | ✅ CDC 必须 | ⚠️ 部分可用 |
| 函数不确定性 | 存在问题 | 无 | 自动规避 |
| 大事务影响 | 小 | 大 | 中 |
| 审计可读性 | ✅ 高 | ❌ 二进制 | 中 |
| MySQL 默认 | 5.1 前 | 8.0 默认 | - |
⚠️ 所有 CDC 工具(Debezium / Canal / Maxwell)都必须使用 ROW 格式。
五、主从复制原理
5.1 复制架构(三线程模型)
┌─────────────────┐ ┌─────────────────┐
│ 主库 Master │ │ 从库 Slave │
│ │ binlog 网络传输 │ │
│ ┌───────────┐ │ ───────────────────────► │ ┌───────────┐ │
│ │ Binlog │ │ │ │ I/O Thread │ │
│ │ Dump Thread│ │ │ └─────┬─────┘ │
│ └───────────┘ │ │ │ │
│ │ │ ┌─────▼─────┐ │
│ │ │ │ Relay Log │ │
│ │ │ └─────┬─────┘ │
│ │ │ │ │
│ │ │ ┌─────▼─────┐ │
│ │ │ │ SQL Thread │ │
│ │ │ └───────────┘ │
│ │ │ │
└─────────────────┘ └─────────────────┘
主库线程:
| 线程 | 职责 |
|---|---|
| Binlog Dump | 监听从库的 I/O 线程请求,读取 binlog 发送给从库 |
从库线程:
| 线程 | 职责 |
|---|---|
| I/O Thread | 连接主库,请求 binlog,接收并写入本地 Relay Log |
| SQL Thread | 读取 Relay Log,按顺序执行 SQL,应用到从库 |
5.2 复制流程(详细步骤)
1. 从库执行 START SLAVE,I/O Thread 向主库发起 binlog 请求
2. 主库 Binlog Dump Thread 根据请求的 position,读取 binlog 事件并发送
3. 从库 I/O Thread 接收事件,写入本地 Relay Log(顺序追加)
4. 从库 SQL Thread 读取 Relay Log,按顺序重放 SQL(应用变更)
5. I/O Thread 维护一个 master.info 文件,记录已读取到的 binlog position
6. SQL Thread 维护一个 relay-log.info 文件,记录已执行的 position
5.3 异步复制与半同步复制
| 复制模式 | 行为 | 数据安全性 |
|---|---|---|
| 异步复制(默认) | 主库提交事务后立即返回,不等从库确认 | ⚠️ 主库故障时部分数据可能未同步到从库 |
| 半同步复制 | 主库提交后,等待至少一个从库写入 Relay Log 并响应后才返回 | ✅ 至少一从库有数据保障 |
| 全同步复制 | 主库提交后,等待所有从库都应用完才返回 | ✅ 最高安全,但性能差 |
半同步配置(参数):
-- 主库安装插件
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 10000; -- 等待10秒超时
-- 从库安装插件
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
六、GTID 模式(全局事务 ID)
6.1 什么是 GTID
GTID(Global Transaction Identifier)= {source_id}:{transaction_id},每个在主库提交的事务都有一个全局唯一 ID。
示例:0d3fa0ec-6c3b-11ef-9c8e-0242ac110002:1-99
→ source_id = server_uuid(服务器唯一标识)
→ transaction_id = 事务序号(1到99)
6.2 GTID 工作流程
主库执行事务:
BEGIN;
UPDATE products SET price=2999 WHERE id=1;
COMMIT;
→ 生成 GTID: uuid:1-99
主库记录到 binlog:
GTID_LOG_EVENT: {uuid:1-99}
QUERY_EVENT: BEGIN
TABLE_MAP_EVENT: products
UPDATE_ROWS_EVENT: ...
XID_EVENT: COMMIT
从库接收并执行:
SET GTID_NEXT = 'uuid:1-99';
BEGIN;
-- 应用变更
COMMIT;
SET GTID_NEXT = AUTOMATIC;
6.3 GTID 的优势
| 优势 | 说明 |
|---|---|
| 自动位点定位 | 不需要手动指定 MASTER_LOG_FILE 和 MASTER_LOG_POS,从库自动找到正确位置 |
| 故障转移简单 | 新主库可以直接从 GTID 恢复,无需手动计算 position |
| 一致性保证 | 每个事务有唯一 ID,不会重复也不会遗漏 |
| CDC 必需 | Debezium 支持 GTID 模式,消费时自动处理位点 |
6.4 GTID 配置
# my.cnf 主库
gtid_mode = ON
enforce_gtid_consistency = ON
log_bin = mysql-bin
binlog_format = ROW
-- 从库配置
CHANGE MASTER TO
MASTER_HOST = 'master-host',
MASTER_USER = 'repl_user',
MASTER_PASSWORD = 'xxx',
MASTER_AUTO_POSITION = 1; -- 开启 GTID 自动位点
七、Binlog 关键配置参数
7.1 必须配置(CDC 场景)
# my.cnf
# 开启 binlog
log_bin = mysql-bin
# 格式必须为 ROW(CDC 必须)
binlog_format = ROW
# 记录完整行数据(ROW 格式下推荐)
binlog_row_image = FULL
# 服务器唯一 ID(主从复制必须不同)
server_id = 1
# binlog 保留天数(根据磁盘空间和数据量调整)
expire_logs_days = 7
# 最大 binlog 文件大小(默认 1GB)
max_binlog_size = 1073741824
# sync_binlog 控制刷盘策略
# =1:每事务提交都同步到磁盘(最安全,性能最低)
# =0:由 OS 控制刷盘(性能高,可能丢失1秒内日志)
sync_binlog = 1
7.2 可选优化配置
# 记录表是否写入 binlog(CDC 场景通常关闭,减少噪音)
binlog_rows_query_log_events = ON # 在 ROW 格式下记录原始 SQL
# gtid 模式(CDC 推荐开启)
gtid_mode = ON
enforce_gtid_consistency = ON
# 开启只读(从库设置,防止意外写入)
read_only = ON
super_read_only = ON
7.3 Binlog 相关命令
-- 查看当前 binlog 状态
SHOW MASTER STATUS;
-- 输出:
-- File: mysql-bin.000123
-- Position: 45678
-- Binlog_Do_DB: ecommerce
-- Executed_Gtid_Set: uuid:1-99
-- 查看所有 binlog 文件列表
SHOW BINARY LOGS;
-- 或
SHOW MASTER LOGS;
-- 查看指定 binlog 文件内容(可读格式)
SHOW BINLOG EVENTS IN 'mysql-bin.000123' LIMIT 10;
SHOW BINLOG EVENTS IN 'mysql-bin.000123' FROM 100 LIMIT 10;
-- 查看当前实例所有 GTID 事务
SELECT * FROM mysql.gtid_executed;
-- 刷新 binlog(生成新文件,切割日志)
FLUSH BINARY LOGS;
-- 清理 binlog(谨慎操作)
PURGE BINARY LOGS TO 'mysql-bin.000150'; -- 删到指定文件
PURGE BINARY LOGS BEFORE '2026-01-01 00:00:00'; -- 删到指定时间
7.4 Binlog 文件内容查看工具
# 1. mysqlbinlog(MySQL 自带,最常用)
mysqlbinlog mysql-bin.000123
mysqlbinlog --base64-output=DECODE-ROWS mysql-bin.000123 # ROW格式需要解码
mysqlbinlog --stop-never mysql-bin.000123 # 实时跟踪
# 2. 查看 ROW 格式详细变更
mysqlbinlog -vv --base64-output=DECODE-ROWS mysql-bin.000123
# 3. 指定 position 范围
mysqlbinlog mysql-bin.000123 --start-position=100 --stop-position=500
# 4. 指定时间范围
mysqlbinlog mysql-bin.000123 --start-datetime='2026-01-01 10:00:00' \
--stop-datetime='2026-01-01 12:00:00'
八、Binlog 在 CDC 中的角色
8.1 CDC 工具如何读取 Binlog
┌────────────────────────────────────────────────────┐
│ MySQL 主库 │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Binlog (ROW 格式) │ │
│ │ ├── GTID_LOG_EVENT │ │
│ │ ├── TABLE_MAP_EVENT (products) │ │
│ │ ├── UPDATE_ROWS_EVENT (before/after) │ │
│ │ └── XID_EVENT (COMMIT) │ │
│ └──────────────────┬─────────────────────────┘ │
└──────────────────────┼────────────────────────────┘
│ MySQL Replication Protocol
┌──────────────┴──────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Debezium │ │ Canal │
│ (伪装从库) │ │ (伪装从库) │
│ │ │ │
│ Binlog Reader │ │ Parser │
│ 基于MySQL库 │ │ (binlog协议解析) │
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
Kafka Topic Kafka / MQ / ES Adapter
CDC 工具并不是读取 binlog 文件,而是伪装成 MySQL 从库,通过 MySQL 主从复制协议接收 binlog 事件流。
8.2 CDC 需要的最小权限
CREATE USER 'cdc_user'@'%' IDENTIFIED BY 'password';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'cdc_user';
FLUSH PRIVILEGES;
| 权限 | 用途 |
|---|---|
REPLICATION SLAVE |
伪装从库,连接主库读取 binlog |
REPLICATION CLIENT |
查询 binlog 状态(SHOW MASTER STATUS 等) |
SELECT |
做 Initial Snapshot 时读取表数据 |
九、常见问题
Q1:Binlog 文件满了怎么办?
设置 max_binlog_size(默认 1GB)后会自动切换到下一个文件。如果磁盘空间不足,MySQL 会报错无法写入,此时需要删除旧文件或扩展磁盘空间。
Q2:Binlog 占用磁盘过大怎么清理?
-- 自动清理(按 expire_logs_days)
PURGE BINARY LOGS TO 'mysql-bin.000150'; -- 手动清理到指定文件
-- 删除所有 binlog(危险,等同于重置主从)
RESET MASTER;
Q3:Binlog 格式选错了能在线改吗?
-- 在线修改(无需重启)
SET GLOBAL binlog_format = ROW;
SET GLOBAL binlog_row_image = FULL;
-- 但注意:只对新建连接生效,已有连接不受影响
Q4:从库延迟很大,怎么排查?
-- 在从库执行,查看延迟
SHOW SLAVE STATUS\G
-- 关键字段:
-- Seconds_Behind_Master: 0 (延迟秒数)
-- Relay_Log_Space: 10485760 (Relay Log 总量)
-- Slave_IO_Running: Yes
-- Slave_SQL_Running: Yes
常见原因:大事务、SQL Thread 单线程执行、从库服务器性能差、主库写入过密。
Q5:ROW 格式下 DELETE 怎么知道删的是哪一行?
ROW 格式的 DELETE_ROWS_EVENT 会记录被删除行的完整数据(不仅仅是主键),因此 CDC 工具可以精确还原每条删除操作。从 MySQL 8.0.1 开始,还可以额外记录主键值用于快速匹配。
本文档系统梳理了 MySQL binlog 的原理、结构、格式与 CDC 关系,如需继续了解 binlog 与具体 CDC 工具的对接细节,可进一步展开。