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,需要:
- 从
TABLE_MAP_EVENT提取列信息; - 从
WRITE_ROWS_EVENT解析行数据(含列值); - 根据表结构拼接 SQL;
- 注意字符串转义、NULL处理等细节。
建议用现成库(例如 python-mysql-replication)完成这部分,它会自动帮你映射字段。
















