数据库系列(八):复制与高可用 — 主从、半同步、MGR、AlwaysOn、ADG

写在前面

承接前一篇日志,本文进入"复制与高可用"——单机数据库解决不了的问题。

本文要回答:

主从怎么同步?半同步和异步有什么区别?为什么 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 的差异。