主题
概念卡片: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(中转日志) |
| 5 | sql_thread 解析 relay log 并执行 |
备库建议设 readonly:防误操作 + 防切换双写 + 用状态判角色。但 readonly 对 super 权限无效,同步线程正好用 super 权限写入。
binlog 三种格式
| 格式 | 内容 | 优点 | 缺点 |
|---|---|---|---|
| statement | SQL 原文 | 省空间 | 可能主备不一致(如 delete 带 limit,主备用不同索引删不同行) |
| row | Table_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:
- 两库 server id 必须不同
- 备库重放生成的 binlog 保留原 server id
- 收到 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 集合里」,在就跳过 → 从根本上避免循环复制
常见误解(避坑)
- ❌ "statement 格式的 binlog 就能保证主备一致"。→ statement 记 SQL 原文,主备可能用不同索引执行出不同结果(delete 带 limit 是典型)。
- ❌ "row 格式只是更安全,没有别的用"。→ row 能恢复误删数据(delete 转 insert 回放),这是闪回工具的基础。
- ❌ "循环复制靠 server id 就万无一失"。→ server id 被改会失效,GTID 才是根本解法。
- ❌ "主从是同步的"。→ 默认异步复制,主库不等备库确认就返回,主从有延迟。
关联
- 原始资料:24|MySQL是怎么保证主备一致的? · 23|MySQL是怎么保证数据不丢的?
- 总览:MySQL技术栈总览
- 相关卡:概念卡片:MySQL日志与两阶段提交(binlog 的写入机制)· 概念卡片:MySQL读写分离与过期读(主从延迟的后果)
- 域地图:A00-百科/数据与存储/数据与存储