Skip to content

主备一致性

一、 引言:Binlog 的核心地位

Binlog (二进制日志) 是 MySQL 实现高可用 (HA) 架构(如主从复制、主备切换)和数据恢复(归档、Point-in-Time Recovery)的基石。理解 Binlog 的工作原理对于保障数据一致性至关重要。

二、 MySQL 主备复制基本原理

  1. 架构模式:
    • 主从 (Master-Slave, M-S): 主库 (Master) 处理读写,备库 (Slave) 接收主库 Binlog 并执行,保持数据同步。备库通常设为 readonly
    • 双主 (Master-Master, M-M): 互为主备,切换时无需修改主备关系,但需解决循环复制问题。
  2. 备库 readonly 的意义:
    • 防误操作: 避免在备库上执行计划外的写操作(如运营查询误操作)。
    • 防双写: 防止切换逻辑 Bug 导致切换瞬间两边都写入,造成数据不一致。
    • 角色判断: 可通过 readonly 状态识别节点角色。
    • 不影响复制: readonly 对具有 SUPER 权限的复制线程无效。
  3. 复制流程:
    • 连接建立: 备库通过 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() 有特殊处理)。
    • 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 明确。

数据恢复注意: 不能简单地从 STATEMENTMIXED 格式的 Binlog 中拷贝 SQL 语句来恢复,可能丢失 SET TIMESTAMP 等上下文信息导致错误。标准做法是使用 mysqlbinlog 工具解析后,将完整解析结果传给 MySQL 执行。

四、 循环复制问题与解决 (双 M 架构)

  • 问题: 在双 M 架构中,A 的更新传给 B,B 执行后生成 Binlog (需 log_slave_updates=on),B 的 Binlog 又传回 A,A 执行后再次生成 Binlog... 形成无限循环。
  • 解决方案:利用 server_id
    1. 不同 Server ID: 每个 MySQL 实例必须配置唯一server_id
    2. Binlog 记录来源: Binlog 事件中会记录最初执行该事务的实例的 server_id
    3. 备库判断: 备库在接收到来自主库的 Binlog 事件时,会检查事件中的 server_id
    4. 丢弃自身日志: 如果事件的 server_id 与备库自身server_id 相同,说明该事件是自己产生的,则直接丢弃,不再执行,从而打破循环。

五、 总结

  • Binlog 是 MySQL HA 的核心,通过日志复制和重放保证主备一致性。
  • Binlog 格式选择至关重要:STATEMENT 有风险,ROW 最安全且利于恢复,MIXED 是折中。现代应用推荐使用 ROW 格式。
  • 双 M 架构通过 server_id 机制避免循环复制。
  • 理解 Binlog 机制有助于设计高可用架构和进行数据恢复。