x

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                全局事务IDGTID模式
├── 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;   ROWNOW()不确定)
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 SLAVEI/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_FILEMASTER_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 工具的对接细节,可进一步展开。

Left-click: follow link, Right-click: select node, Scroll: zoom
x