Skip to content

概念卡片:MySQL 主从复制与 binlog

一句话机制

MySQL 几乎所有高可用架构都依赖 binlog:主库写 binlog,备库用 io_thread 拉取、写入 relay log、再由 sql_thread 重放,从而和主库保持一致。 binlog 的格式(statement/row/mixed)决定了复制的安全性与体积,GTID 解决循环复制的缺陷。

主备基本原理

主库 A(读写)──── binlog ────→ 备库 B(readonly)
                  io_thread 拉取 → relay log → sql_thread 重放
步骤动作
1备库 change master 设置主库 IP/端口/账号 + 起始位点
2备库 start slave 启动 io_thread(连主库)+ sql_thread(重放)
3主库校验后,从指定位点读 binlog 发给备库
4备库写 relay log(中转日志)
5sql_thread 解析 relay log 并执行

备库建议设 readonly:防误操作 + 防切换双写 + 用状态判角色。但 readonly 对 super 权限无效,同步线程正好用 super 权限写入。

binlog 三种格式

格式内容优点缺点
statementSQL 原文省空间可能主备不一致(如 delete 带 limit,主备用不同索引删不同行)
rowTable_map + Delete_rows 等 event(记真实行数据)绝对一致、可恢复数据占空间、写 IO 大
mixed自动判断,可能不一致用 row 否则 statement折中已被 row 取代

生产建议至少 mixed,趋势是直接 row:row 格式能恢复误删数据(delete 转 insert、update 前后对调),MariaDB Flashback 就是基于此原理。

循环复制与 server id

双 M 结构(A↔B 互为主备)下,A 的更新传到 B 执行,B 又生成 binlog 传回 A,会无限循环。解决靠 server id

  1. 两库 server id 必须不同
  2. 备库重放生成的 binlog 保留原 server id
  3. 收到 binlog 先比 server id,与自己相同就丢弃

⚠️ server id 机制有缺陷:运维改 server id 会导致判断失效(如 A 从 1 改成 3,收到自己原本 id=1 的日志会误执行)。MySQL 5.6 引入 GTID 解决。

GTID(Global Transaction ID)

GTID = source_uuid : transaction_id
  • 每个实例有唯一 UUID(存在 auto.cnf,永不变)
  • 事务无论被转发多少次,UUID 永远是生成它的实例的 UUID
  • 备库判断「这个 GTID 是否已在 gtid_executed 集合里」,在就跳过 → 从根本上避免循环复制

常见误解(避坑)

  1. ❌ "statement 格式的 binlog 就能保证主备一致"。→ statement 记 SQL 原文,主备可能用不同索引执行出不同结果(delete 带 limit 是典型)。
  2. ❌ "row 格式只是更安全,没有别的用"。→ row 能恢复误删数据(delete 转 insert 回放),这是闪回工具的基础。
  3. ❌ "循环复制靠 server id 就万无一失"。→ server id 被改会失效,GTID 才是根本解法。
  4. ❌ "主从是同步的"。→ 默认异步复制,主库不等备库确认就返回,主从有延迟。

关联

最近更新