主备一致性
一、 引言:Binlog 的核心地位
Binlog (二进制日志) 是 MySQL 实现高可用 (HA) 架构(如主从复制、主备切换)和数据恢复(归档、Point-in-Time Recovery)的基石。理解 Binlog 的工作原理对于保障数据一致性至关重要。
二、 MySQL 主备复制基本原理
- 架构模式:
- 主从 (Master-Slave, M-S): 主库 (Master) 处理读写,备库 (Slave) 接收主库 Binlog 并执行,保持数据同步。备库通常设为
readonly
。 - 双主 (Master-Master, M-M): 互为主备,切换时无需修改主备关系,但需解决循环复制问题。
- 主从 (Master-Slave, M-S): 主库 (Master) 处理读写,备库 (Slave) 接收主库 Binlog 并执行,保持数据同步。备库通常设为
- 备库
readonly
的意义:- 防误操作: 避免在备库上执行计划外的写操作(如运营查询误操作)。
- 防双写: 防止切换逻辑 Bug 导致切换瞬间两边都写入,造成数据不一致。
- 角色判断: 可通过
readonly
状态识别节点角色。 - 不影响复制:
readonly
对具有SUPER
权限的复制线程无效。
- 复制流程:
- 连接建立: 备库通过
CHANGE MASTER TO ...
指定主库信息及 Binlog 起始位置 (文件名 + Position)。 - 线程启动: 备库执行
START SLAVE
,启动两个关键线程:io_thread
: 连接主库,请求指定位置后的 Binlog。sql_thread
: 读取relay log
并执行其中的事件。 (现代版本可能有多线程sql_thread
)
- 日志传输: 主库验证身份后,发送 Binlog 给备库的
io_thread
。 - 中转存储: 备库
io_thread
将接收到的 Binlog 写入本地的中转日志 (Relay Log)。 - 日志重放: 备库
sql_thread
读取relay log
,解析 SQL 或行事件,并在备库上执行,实现数据同步。
- 连接建立: 备库通过
三、 Binlog 的三种格式详解
1. Statement 格式 (binlog_format=STATEMENT
)
- 内容: 记录原始的 SQL 语句原文。
- 优点: 日志量小,节省磁盘空间和网络带宽。
- 缺点:
- 可能导致主备不一致:
- 对于依赖上下文的 SQL(如
DELETE ... LIMIT
),在主备库可能因优化器选择不同索引而删除不同行。 - 使用非确定性函数(如
UUID()
,NOW()
- 虽然NOW()
有特殊处理)。
- 对于依赖上下文的 SQL(如
- MySQL 会将某些它认为不安全的 Statement 标记为
unsafe
。
- 可能导致主备不一致:
- 结论: 不推荐使用,存在数据一致性风险。
2. Row 格式 (binlog_format=ROW
)
- 内容: 不记录 SQL 原文,记录实际数据行的变更。
Table_map Event
: 标识要操作的表。Write_rows Event
(INSERT): 记录插入行的所有列数据。Update_rows Event
(UPDATE): 记录修改前和修改后行的所有列数据(取决于binlog_row_image
参数,默认为FULL
)。Delete_rows Event
(DELETE): 记录被删除行的所有列数据(取决于binlog_row_image
参数)。
- 优点:
- 保证主备数据绝对一致: 精确记录行变更,无歧义。
- 利于数据恢复/闪回:
- 误
DELETE
:可将Delete_rows
事件转换为INSERT
恢复数据。 - 误
INSERT
:可将Write_rows
事件转换为DELETE
删除误插入数据。 - 误
UPDATE
:可将Update_rows
事件中的前后镜像对调,执行反向UPDATE
恢复数据。
- 误
- 缺点: 日志量大,占用更多磁盘空间和网络带宽,可能增加 IO 负担。
- 结论: 当前推荐格式,尤其是在意数据一致性和恢复能力的场景。
3. Mixed 格式 (binlog_format=MIXED
)
- 内容: MySQL 自动判断,对可能引起主备不一致的 SQL 使用
ROW
格式,其他情况使用STATEMENT
格式。 - 优点: 试图结合 Statement 和 Row 的优点,平衡日志大小和数据一致性。
- 特殊处理
NOW()
: 对于包含NOW()
的 Statement,Mixed 格式会记录SET TIMESTAMP=xxx;
命令,确保备库执行时使用相同的时间戳,保证一致性。 - 风险: 判断逻辑复杂,且随着
ROW
格式的普及和其带来的恢复便利性,MIXED
已较少作为首选。 - 结论: 至少比
STATEMENT
好,但通常不如直接使用ROW
明确。
数据恢复注意: 不能简单地从 STATEMENT
或 MIXED
格式的 Binlog 中拷贝 SQL 语句来恢复,可能丢失 SET TIMESTAMP
等上下文信息导致错误。标准做法是使用 mysqlbinlog
工具解析后,将完整解析结果传给 MySQL 执行。
四、 循环复制问题与解决 (双 M 架构)
- 问题: 在双 M 架构中,A 的更新传给 B,B 执行后生成 Binlog (需
log_slave_updates=on
),B 的 Binlog 又传回 A,A 执行后再次生成 Binlog... 形成无限循环。 - 解决方案:利用
server_id
- 不同 Server ID: 每个 MySQL 实例必须配置唯一的
server_id
。 - Binlog 记录来源: Binlog 事件中会记录最初执行该事务的实例的
server_id
。 - 备库判断: 备库在接收到来自主库的 Binlog 事件时,会检查事件中的
server_id
。 - 丢弃自身日志: 如果事件的
server_id
与备库自身的server_id
相同,说明该事件是自己产生的,则直接丢弃,不再执行,从而打破循环。
- 不同 Server ID: 每个 MySQL 实例必须配置唯一的
五、 总结
- Binlog 是 MySQL HA 的核心,通过日志复制和重放保证主备一致性。
- Binlog 格式选择至关重要:
STATEMENT
有风险,ROW
最安全且利于恢复,MIXED
是折中。现代应用推荐使用ROW
格式。 - 双 M 架构通过
server_id
机制避免循环复制。 - 理解 Binlog 机制有助于设计高可用架构和进行数据恢复。