1️⃣ binlog字节级结构图(可视化解释每个部分的偏移与作用)
2️⃣ 一个最小可运行的 Python binlog 解析示例(从字节流中读取事件头与类型)


🧩 一、MySQL binlog 二进制结构示意图

一个典型的 mysql-bin.000001 文件结构如下:

┌────────────────────────────────────────────┐
│ File Header (17 bytes)                     │
│   ├─ 4 bytes  Magic number: 0xFE 62 69 6E  │
│   └─ 13 bytes Reserved                     │
├────────────────────────────────────────────┤
│ FORMAT_DESCRIPTION_EVENT                   │
│   ├─ 19 bytes Event Header                 │
│   └─ Body (描述binlog版本、server版本等)    │
├────────────────────────────────────────────┤
│ [可选] Previous_GTIDs_EVENT                │
├────────────────────────────────────────────┤
│ QUERY_EVENT                                │
│   ├─ Event Header                          │
│   ├─ Thread ID (4B)                        │
│   ├─ Exec Time (4B)                        │
│   ├─ Schema Length (1B)                    │
│   ├─ Error Code (2B)                       │
│   ├─ Status Vars (lenenc)                  │
│   ├─ Schema Name (variable)                │
│   ├─ SQL Statement (variable)              │
├────────────────────────────────────────────┤
│ TABLE_MAP_EVENT                            │
│   ├─ Event Header                          │
│   ├─ Table ID (6B)                         │
│   ├─ Flags (2B)                            │
│   ├─ Schema Name Length + Value            │
│   ├─ Table Name Length + Value             │
│   ├─ Column Count + Column Types           │
│   └─ Column Metadata + NULL bitmap         │
├────────────────────────────────────────────┤
│ WRITE_ROWS_EVENT                           │
│   ├─ Event Header                          │
│   ├─ Table ID (6B)                         │
│   ├─ Flags (2B)                            │
│   ├─ Columns Count (lenenc)                │
│   ├─ Columns Bitmap (n bytes)              │
│   ├─ Rows Data (包含每行每列值)              │
├────────────────────────────────────────────┤
│ XID_EVENT                                  │
│   ├─ Event Header                          │
│   └─ XID (事务提交标识)                    │
└────────────────────────────────────────────┘

⚙️ 二、Python简易binlog字节解析器示例

这个脚本不会依赖外部库,只演示如何从二进制文件读取事件头,识别事件类型,并打印基本信息。

import struct

# MySQL binlog event type codes(部分)
EVENT_TYPES = {
    2: "QUERY_EVENT",
    15: "WRITE_ROWS_EVENT",
    16: "UPDATE_ROWS_EVENT",
    17: "DELETE_ROWS_EVENT",
    19: "FORMAT_DESCRIPTION_EVENT",
    23: "TABLE_MAP_EVENT",
    27: "XID_EVENT",
}

def read_event_header(f):
    """读取19字节的通用事件头"""
    header = f.read(19)
    if len(header) < 19:
        return None  # EOF
    timestamp, event_type, server_id, event_size, log_pos, flags = struct.unpack("<IBIIIH", header)
    return {
        "timestamp": timestamp,
        "event_type": event_type,
        "server_id": server_id,
        "event_size": event_size,
        "log_pos": log_pos,
        "flags": flags
    }

def parse_binlog(filepath):
    with open(filepath, "rb") as f:
        # 跳过文件头17字节
        magic = f.read(4)
        if magic != b'\xfe\x62\x69\x6e':
            print("不是合法的binlog文件!")
            return
        f.read(13)

        print("✔ Magic Header OK, 开始读取事件...\n")

        while True:
            pos = f.tell()
            header = read_event_header(f)
            if not header:
                break

            event_type = header["event_type"]
            name = EVENT_TYPES.get(event_type, f"UNKNOWN({event_type})")
            body_size = header["event_size"] - 19
            body = f.read(body_size)

            print(f"[{pos:08x}] {name}  size={header['event_size']}  next={header['log_pos']}")

        print("\n✔ 文件读取结束")

# 使用示例
# parse_binlog("/var/lib/mysql/mysql-bin.000001")

运行输出(示例):

✔ Magic Header OK, 开始读取事件...

[00000011] FORMAT_DESCRIPTION_EVENT  size=123  next=134
[00000086] QUERY_EVENT               size=89   next=223
[000000e0] TABLE_MAP_EVENT           size=53   next=276
[0000011d] WRITE_ROWS_EVENT          size=85   next=361
[0000015a] XID_EVENT                 size=31   next=392

✔ 文件读取结束

你可以在此基础上继续深入:

  • 进一步解析 TABLE_MAP_EVENT 的表名;
  • 解码 WRITE_ROWS_EVENT 的行数据;
  • 结合表结构反推出完整 SQL。

🧱 三、如果要完整还原 SQL

要得到完整的 INSERT/UPDATE/DELETE SQL,需要:

  1. TABLE_MAP_EVENT 提取列信息;
  2. WRITE_ROWS_EVENT 解析行数据(含列值);
  3. 根据表结构拼接 SQL;
  4. 注意字符串转义、NULL处理等细节。

建议用现成库(例如 python-mysql-replication)完成这部分,它会自动帮你映射字段。