写在前面
承接前一篇日志,本文进入"复制与高可用"——单机数据库解决不了的问题。
本文要回答:
主从怎么同步?半同步和异步有什么区别?为什么 MySQL 有 MGR、SQL Server 有 AlwaysOn、Oracle 有 RAC、PG 有流复制?脑裂怎么防?Paxos 和 Raft 是什么?
一、为什么需要复制
1.1 单机的瓶颈
1
2
3
4
5
6
7
8
9
10
11
|
单机数据库的天花板:
- 容量:单机磁盘上限(TB 级)
- 性能:单机 CPU/内存(万级 QPS)
- 可用性:单点故障
- 地理分布:跨地域延迟
复制解决:
- 高可用:主库挂了切从库
- 读扩展:读分摊到多个从库
- 备份:从库做统计/备份,不影响主库
- 跨地域:在多地部署从库
|
1.2 复制的基本问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
1. 复制什么?
- 物理日志(Redo/WAL)
- 逻辑日志(Binlog/逻辑复制)
2. 同步还是异步?
- 异步:主写完就返回,不等从
- 同步:主等所有从确认才返回
- 半同步:主等至少一个从确认
3. 谁来选主?
- 人工指定(传统主从)
- 自动选主(MGR / AlwaysON / Patroni)
4. 怎么处理脑裂?
- Quorum(多数派)
- Fencing(隔离故障主)
- 见证服务器
|
二、物理复制 vs 逻辑复制
2.1 物理复制
1
2
3
4
5
6
7
8
9
10
11
|
复制"字节流"——主库 Redo/WAL 直接传给从库
主库 从库
Redo Log ─────────────────→ 应用到本地数据页
特点:
✅ 速度快(字节流直接应用)
✅ 一致性强(页级精确)
❌ 从库必须同版本、同平台
❌ 不能跨大版本
❌ 不能选择性复制
|
2.2 逻辑复制
1
2
3
4
5
6
7
8
9
10
11
12
|
复制"逻辑变更"——主库解析出"哪行被改了",传给从库
主库 从库
Binlog / 逻辑日志 ────────→ 解析为 INSERT/UPDATE/DELETE
特点:
✅ 跨版本(甚至跨数据库引擎)
✅ 可选择性复制(按表 / 库)
✅ 异构系统(OLTP→OLAP)
❌ 性能略低于物理复制
❌ DDL 不会自动复制(PG)
❌ 一致性不如物理复制
|
2.3 四家选择
| 数据库 |
默认复制方式 |
| Oracle |
物理(Data Guard)+ 逻辑(GoldenGate) |
| SQL Server |
物理(AlwaysOn) |
| MySQL |
逻辑(Binlog,传统主从) |
| PostgreSQL |
物理(流复制)+ 逻辑(10+) |
三、MySQL 复制体系
3.1 传统异步复制
1
2
3
4
5
6
7
8
9
10
11
|
架构:
Master(Binlog)──────→ IO Thread(Slave)──→ Relay Log
↓
SQL Thread(Slave)
↓
应用到数据
特点:
- 主库写完 Binlog 即返回,不等从库确认
- 从库异步拉取 Binlog
- 可能丢数据(主库崩溃时未同步的事务)
|
3.2 半同步复制(Semi-Sync)
1
2
3
4
5
6
7
8
9
10
11
|
5.5+ 引入,5.7+ 增强
工作流程:
1. 主库写 Binlog
2. 主库 COMMIT 时,至少一个从库 ACK
3. 主库收到 ACK 后才返回客户端成功
特点:
- 主库挂了不丢已 ACK 的事务
- 至少一个从库有数据
- 性能比异步慢(多等一个网络往返)
|
1
2
3
4
5
6
7
8
|
-- 主库
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 1000; -- ms,超时降级为异步
-- 从库
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
|
3.3 组复制(MGR)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
MySQL 8.0+ 提供的高可用方案
基于 Paxos 协议
架构:
Primary ─────┐
│
Secondary ───┤ 组(Group)
│
Secondary ───┘
所有成员通过 Paxos 同步事务
特点:
- 多主复制(可以多 Primary 写入)
- 自动故障检测和选主
- 强一致性(事务提交需多数派确认)
- 至少 3 节点(容忍 1 节点失败)
要求:
- 必须用 GTID
- 表必须有主键
- 默认 RR 隔离级别
- 不支持外键、Savepoint 跨节点
|
1
2
3
4
5
6
7
8
9
|
# my.cnf 示例
[mysqld]
gtid_mode = ON
enforce_gtid_consistency = ON
plugin_load_add = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
group_replication_local_address = "node1:33061"
group_replication_group_seeds = "node1:33061,node2:33061,node3:33061"
transaction_write_set_extraction = XXHASH64
|
3.4 三种方案对比
| 方案 |
一致性 |
性能 |
自动故障转移 |
部署复杂度 |
| 异步复制 |
弱 |
高 |
❌ |
简单 |
| 半同步复制 |
中 |
中 |
❌ |
中 |
| MGR |
强 |
中 |
✅ |
复杂 |
1
2
3
4
|
选型建议:
- 互联网 OLTP(容忍少量数据丢失):异步 + MHA
- 重要业务(要数据安全):半同步 + Orchestrator
- 关键业务(要求强一致):MGR(或上 TiDB)
|
四、PostgreSQL 复制
4.1 流复制(Streaming Replication)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
物理复制,基于 WAL
架构:
Primary ────WAL stream────→ Standby(连续应用)
工作流程:
1. Primary 写 WAL
2. WAL Sender 进程推送给 Standby
3. Standby 的 WAL Receiver 接收
4. Standby 进程应用 WAL
特点:
- 物理字节流
- 支持同步 / 异步
- 支持"级联复制"(Standby 也能转发)
- Standby 默认只读(Hot Standby)
|
1
2
3
4
5
6
7
8
|
# postgresql.conf
wal_level = replica # 或 logical
synchronous_commit = on
synchronous_standby_names = '*' # 同步复制(指定 Standby 名)
# Standby recovery.conf / postgresql.auto.conf
primary_conninfo = 'host=primary port=5432'
hot_standby = on # 允许在恢复中查询
|
4.2 同步复制
1
2
3
4
5
6
|
PG 同步复制级别(synchronous_commit):
remote_apply = 等从库应用完事务(最严格)
remote_flush = 等从库 fsync WAL
remote_write = 等从库写 OS Cache
local = 只等本地 fsync
off = 不等任何东西
|
4.3 逻辑复制(10+)
1
2
3
4
5
6
7
8
9
10
|
基于"Publication / Subscription"模型
发布者(主库):
CREATE PUBLICATION pub_orders FOR TABLE orders;
-- 只发布 orders 表
订阅者(从库):
CREATE SUBSCRIPTION sub_orders
CONNECTION 'host=primary'
PUBLICATION pub_orders;
|
1
2
3
4
5
6
7
8
9
10
|
逻辑复制用途:
- 选择性复制(只复制部分表)
- 异构系统(PG → Doris / ClickHouse)
- 滚动升级(旧版 → 新版)
- 多活双向(Bidirectional,复杂)
限制:
- 不复制 DDL(要手动同步)
- 不复制大对象
- 没有 schema 一致性保证
|
4.4 PG 高可用方案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
PG 本身不提供"自动故障转移",需要外部工具:
- Patroni(Zalando 开源,最流行)
基于 etcd / ZooKeeper / Consul
自动选主 + 自动重配
- repmgr(2ndQuadrant 开源)
基于 PG 流复制
简单但功能有限
- pg_auto_failover(Citus 开源)
基于见证节点(monitor)
部署简单
- Stolon(基于 etcd)
类似 Patroni
|
五、SQL Server AlwaysOn
5.1 两种 AlwaysOn
1
2
3
4
5
6
7
8
9
10
11
|
AlwaysOn Availability Group(AG):
- 应用层高可用
- 数据库级故障转移
- 多个副本(1 主 + 8 副)
- 副本可读
AlwaysOn Failover Cluster Instance(FCI):
- 实例层高可用
- 共享存储(SAN)
- 节点级故障转移
- 经典 Windows 集群方案
|
5.2 AG 工作流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
架构:
Primary Replica ──┬──→ Secondary Replica 1(同步)
│
└──→ Secondary Replica 2(异步)
特点:
- 基于 SQL Server Network Name(AD 域)
- Listener:一个虚拟 IP,应用连接它
- 故障自动转移(次要副本升级为 Primary)
- 副本可读(默认 NO,配置后可读)
可用性模式:
SYNCHRONOUS_COMMIT:等副本同步(强一致)
ASYNCHRONOUS_COMMIT:不等副本(性能优先)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
-- 创建 AG(T-SQL)
CREATE AVAILABILITY GROUP MyAG
WITH (
AUTOMATED_BACKUP_PREFERENCE = PRIMARY,
DB_FAILOVER = ON,
DTC_SUPPORT = NONE
)
FOR DATABASE MyDB
REPLICA ON
'Node1' WITH (
ENDPOINT_URL = 'TCP://node1:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC,
SECONDARY_ROLE(ALLOW_CONNECTIONS = ALL)
),
'Node2' WITH (
ENDPOINT_URL = 'TCP://node2:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC,
SECONDARY_ROLE(ALLOW_CONNECTIONS = READ_ONLY)
);
|
六、Oracle 高可用
6.1 Active Data Guard(ADG)
1
2
3
4
5
6
7
8
9
10
11
12
|
物理 Standby,可以同时读
架构:
Primary ──Redo Log Stream──→ Standby(Active)
↓
开放只读查询
特点:
- 物理复制(Redo 流)
- Standby 应用 Redo 时仍可读(Real Apply Time)
- Fast-Start Failover(FSFO):自动故障转移
- 可以"读时备份",主库无压力
|
6.2 Oracle RAC(Real Application Cluster)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
多节点共享存储集群
架构:
┌──────┐ ┌──────┐ ┌──────┐
│Node 1│ │Node 2│ │Node 3│ ← 多实例
└──┬───┘ └──┬───┘ └──┬───┘
│ │ │
└─────────┼─────────┘
│
共享存储(ASM / SAN)
特点:
- 共享存储(所有节点访问同一数据)
- Cache Fusion:节点间内存共享
- 任何节点都能写
- 单节点崩溃不影响业务
- 商业版独有 + 价格昂贵
适用:
- 关键业务(金融核心)
- 高并发 + 不能停服
- 预算充足
|
6.3 Oracle Sharding
1
2
3
4
5
|
12.2+ 引入的分片方案
- 按列分片(hash/range/list)
- 每个分片独立数据库
- 应用通过 Sharding Key 路由
- 类似 MySQL 分库分表
|
七、共识协议
7.1 为什么需要共识
1
2
3
4
5
6
7
8
9
|
分布式系统的根本问题:
- 多个节点如何对一个值达成一致?
- 节点可能崩溃、网络可能延迟、消息可能丢
共识协议(Consensus Algorithm):
- Paxos(1989 Lamport)
- Raft(2014 Ongaro)
- ZAB(ZooKeeper 用)
- Gossip(Cassandra 用,弱一致)
|
7.2 Raft 简化理解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Raft 把共识拆成三部分:
1. Leader 选举
2. 日志复制
3. 安全性
角色:
Leader:唯一写入入口
Follower:被动接受
Candidate:候选人(选举中)
选举:
- Leader 心跳超时 → Follower 成为 Candidate
- Candidate 拉票 → 获多数票 → 成为 Leader
- 一个 Term 最多一个 Leader
日志复制:
1. 客户端请求 → Leader
2. Leader 写本地日志,向 Follower 复制
3. 多数派 ACK → COMMIT
4. 返回客户端
特点:
- 多数派(Majority)原则
- 容忍 (N-1)/2 节点失败
- 3 节点容忍 1 失败,5 节点容忍 2 失败
|
7.3 数据库中的应用
| 数据库 |
共识协议 |
| MySQL MGR |
Paxos 变种(Mencius / EPaxos) |
| CockroachDB |
Raft |
| TiDB / TiKV |
Raft(Multi-Raft) |
| YugabyteDB |
Raft |
| Spanner |
Paxos |
八、脑裂与防护
8.1 什么是脑裂
1
2
3
4
5
6
7
8
9
10
|
场景:网络分区
- 主库和多数派失去联系
- 多数派选了新主
- 旧主以为自己是主,继续接受写
- → 两个主同时写 → 数据冲突
危险:
- 数据不一致(双主写入)
- 业务错乱
- 恢复困难
|
8.2 防护机制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
1. Quorum(多数派)
- 只有获得多数派支持才能成为主
- 网络分区时少数派自动降级
2. Fencing(隔离)
- STONITH(Shoot The Other Node In The Head)
- 物理断电旧主
- PG 的"wal_loghints" + 多数派
3. 见证服务器(Witness)
- 独立第三方仲裁
- SQL Server AlwaysOn 见证
- Patroni 的 DCS(etcd)
4. Split-Brain Avoidance
- 多个心跳路径
- 多数派 + 时间窗口
|
8.3 各家的脑裂处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
MySQL MGR:多数派
- 失去多数派的分区自动进入"只读"
- group_replication_unreachable_majority_timeout
PostgreSQL Patroni:DCS(etcd)+ 多数派
- 主库失去 DCS 连接 → 自动降级为 Standby
- 类似"租约"(Lease)
SQL Server AlwaysOn:Windows Failover Cluster 仲裁
- Node Majority / Node + Disk Witness / Node + File Share Witness
Oracle RAC:CSS(Cluster Synchronization Service)
- 节点驱逐(Node Eviction)
- 网络心跳 + 磁盘心跳
|
九、读写分离
9.1 架构模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
模式 1:应用层路由
Application ──┬──→ Primary(读写)
│
└──→ Standby(只读)
模式 2:代理层路由
Application ──→ Proxy(ProxySQL / HAProxy)
↓
┌──┴──┐
Primary Standby
模式 3:驱动层路由
JDBC URL 配多节点
Connector/J 自动路由
|
9.2 一致性问题
1
2
3
4
5
6
7
8
9
10
|
"写后读"问题:
T1: 用户写评论 → 主库
T2: 用户刷新 → 读从库(延迟)→ 看不到自己的评论
→ 用户体验差
解决方案:
1. 关键路径强制走主库(用户自己的数据)
2. 会话粘滞(写后 5 秒内读主库)
3. 半同步复制降低延迟
4. 业务设计容忍延迟
|
9.3 主从延迟监控
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
-- MySQL
SHOW SLAVE STATUS\G
-- 关注:Seconds_Behind_Master
-- PostgreSQL
SELECT * FROM pg_stat_replication;
-- 关注:write_lag / flush_lag / replay_lag
-- SQL Server
SELECT * FROM sys.dm_hadr_database_replica_states;
-- 关注:log_send_queue_size / redone_queue_size
-- Oracle ADG
SELECT * FROM v$dataguard_stats;
-- 关注:apply lag
|
十、小结
本文学习了复制与高可用:
- 复制动机:高可用、读扩展、备份、跨地域
- 物理复制 vs 逻辑复制
- MySQL 三套方案:异步 / 半同步 / MGR
- PostgreSQL 流复制 + 逻辑复制 + 外部高可用工具
- SQL Server AlwaysOn AG / FCI
- Oracle ADG + RAC + Sharding
- Raft / Paxos 共识协议
- 脑裂防护(Quorum / Fencing / 见证服务器)
- 读写分离与一致性方案
1
2
3
4
|
记住三句话:
1. 强一致 = 性能代价(同步复制慢)
2. 多数派共识是分布式数据库的核心(MGR / TiDB / CockroachDB)
3. 高可用 = 复制 + 自动故障转移 + 脑裂防护,三者缺一不可
|
下一篇进入 SQL 方言与查询优化器:为什么同样的 SQL 在 PG 比 MySQL 快 10 倍?统计信息、执行计划、CBO 的差异。