数据库层跨区复制:MySQL、PostgreSQL 与 MongoDB 的异地容灾实践
数据库层的跨区复制,是整个异地容灾体系里最核心、也最棘手的一环。说白了,你的应用可以无状态地随便漂,但数据不行。数据必须老老实实地待在某个地方,还得在另一个地方有一模一样的副本。
我这些年经手过的容灾项目,数据库层出问题的概率最高。不是方案选型不对,就是细节没抠到位。今天咱们就把 MySQL、PostgreSQL 和 MongoDB 这三兄弟的跨区复制方案掰开揉碎讲清楚。
MySQL 主从复制跨区域部署
MySQL 的主从复制,大家都不陌生。但跨区域部署时,很多坑就冒出来了。
核心原理
主库把变更写入 binlog,从库的 I/O 线程拉过来写到 relay log,然后 SQL 线程回放。就这么简单。但跨区域时,网络延迟会让这个流程变得很脆弱。
关键参数调整
slave_net_timeout:默认 60 秒,跨区域建议调到 10-15 秒,快速感知网络中断sync_binlog=1:每次事务提交都刷盘,保证主库崩溃不丢数据innodb_flush_log_at_trx_commit=1:配合上面的参数,做到真正的 ACID
我曾经遇到过一个案例,主库在北京,从库在深圳。网络偶尔抖动一下,从库就落后了十几分钟。查了半天,发现是 slave_net_timeout 设成了默认的 60 秒,网络闪断后要等一分钟才能重连。改成 10 秒后,问题就解决了。
半同步复制
异步复制有个致命问题:主库挂了,最后几个事务可能没传到从库。半同步复制就是来解决这个的。
-- 主库安装插件
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 = 5000; -- 5秒超时
-- 从库安装插件
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
注意那个超时参数。跨区域时,网络延迟可能超过 100ms,如果超时设得太短,半同步会退化成异步。我建议设成 5-10 秒,既保证数据安全,又不至于因为网络波动频繁降级。
避坑指南
我曾经在跨区域部署时,把半同步超时设成了 1 秒。结果网络稍微一抖,主库就频繁退化成异步模式,等网络恢复后又切回半同步。这种来回切换本身就有风险,而且监控告警响个不停。后来改成 5 秒,世界清净了。
MySQL Group Replication 跨区域部署
Group Replication 是 MySQL 官方的高可用方案,支持多主写入。但跨区域部署时,它的网络要求比主从复制苛刻得多。
组通信的痛点
Group Replication 底层用了 Paxos 协议的变种,需要大多数节点确认才能提交事务。跨区域时,延迟会直接影响写入性能。
| 节点分布 | 写入延迟 | 容灾能力 | 推荐场景 |
|---|---|---|---|
| 同城三机房 | < 5ms | 单机房故障 | 同城容灾 |
| 两地三中心 | 20-50ms | 单区域故障 | 异地容灾 |
| 三地五中心 | 50-100ms | 多区域故障 | 金融级容灾 |
我个人的习惯是,跨区域部署 Group Replication 时,尽量把多数节点放在同一个区域。比如三个节点,两个放在主区域,一个放在备区域。这样大多数事务在主区域内部就能达成共识,跨区域流量只发生在少数场景下。
配置要点
-- 所有节点都要设置
SET GLOBAL group_replication_group_name = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';
SET GLOBAL group_replication_local_address = '公网IP:33061';
SET GLOBAL group_replication_group_seeds = '节点1IP:33061,节点2IP:33061,节点3IP:33061';
-- 跨区域必须调整
SET GLOBAL group_replication_communication_max_message_size = 10485760; -- 10MB
SET GLOBAL group_replication_poll_spin_loops = 10000; -- 减少CPU空转
经验之谈
跨区域部署 Group Replication,一定要把 group_replication_communication_max_message_size 调大。默认是 1MB,跨区域传输大事务时很容易超时。我一般设成 10MB,够用又不浪费内存。
PostgreSQL 流复制与逻辑复制
PostgreSQL 的复制机制比 MySQL 更灵活。流复制适合同城,逻辑复制适合跨区域异构环境。
流复制跨区域部署
流复制是物理级别的,传输的是 WAL 日志。跨区域时,带宽和延迟是主要瓶颈。
-- 主库配置 postgresql.conf
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1024 -- 保留 1GB 的 WAL
-- 从库配置 recovery.conf
primary_conninfo = 'host=主库IP port=5432 user=repl password=xxx'
recovery_target_timeline = 'latest'
跨区域时,我建议开启 synchronous_commit 的 remote_write 模式。它不要求从库刷盘,只要求数据到达操作系统缓存。这样既保证了数据不丢,又不会因为从库刷盘慢而拖慢主库。
流复制 vs 逻辑复制
| 特性 | 流复制 | 逻辑复制 |
|---|---|---|
| 复制粒度 | 整个实例 | 表级别 |
| 版本要求 | 主从版本一致 | 可跨大版本 |
| DDL 支持 | 自动同步 | 需手动处理 |
| 跨区域延迟 | 低 | 中等 |
逻辑复制实战
逻辑复制最大的好处是灵活。你可以只复制某几张表,甚至可以在复制过程中做数据转换。
-- 发布端
CREATE PUBLICATION my_pub FOR TABLE users, orders;
-- 订阅端
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=主库IP port=5432 dbname=mydb user=repl password=xxx'
PUBLICATION my_pub;
我记得有个项目,需要把生产库的部分表实时同步到分析库。生产库是 PostgreSQL 13,分析库是 PostgreSQL 16。用流复制不行,版本不一致。逻辑复制完美解决了这个问题。
注意
逻辑复制不会自动同步 DDL。你在主库加了新列,从库不会自动加。我建议用事件触发器来捕获 DDL,然后通过外部工具同步到从库。或者干脆约定好,DDL 变更走工单流程,两边手动执行。
MongoDB 副本集跨区域配置
MongoDB 的副本集天生支持跨区域部署。它的选举机制和读写偏好设置,让跨区域容灾变得相对简单。
节点分布策略
MongoDB 副本集最多支持 7 个投票节点。跨区域时,我建议这样分布:
- 主区域:2 个数据节点 + 1 个仲裁节点
- 备区域:2 个数据节点
- 第三区域:1 个数据节点(只用于读)
这样配置的好处是,主区域挂了,备区域还有 2 个节点,加上第三区域的 1 个节点,可以组成新的多数派。第三区域的节点设置 priority: 0,永远不会成为主节点,只用于读。
配置示例
// 副本集配置
{
_id: "myReplSet",
members: [
{ _id: 0, host: "主区域节点1:27017", priority: 2 },
{ _id: 1, host: "主区域节点2:27017", priority: 2 },
{ _id: 2, host: "主区域仲裁:27017", arbiterOnly: true },
{ _id: 3, host: "备区域节点1:27017", priority: 1 },
{ _id: 4, host: "备区域节点2:27017", priority: 1 },
{ _id: 5, host: "第三区域节点:27017", priority: 0, hidden: true }
],
settings: {
electionTimeoutMillis: 10000, // 跨区域适当调大
heartbeatIntervalMillis: 2000
}
}
你想想看,跨区域网络延迟高,如果选举超时设得太短,网络抖动就会引发不必要的选举。我一般设成 10 秒,比默认的 5 秒长一倍,稳定性好很多。
读写偏好设置
跨区域部署时,读写偏好直接影响用户体验和一致性。
| 读偏好 | 延迟 | 一致性 | 适用场景 |
|---|---|---|---|
| primary | 高(跨区域) | 强一致 | 写后立即读 |
| primaryPreferred | 中等 | 最终一致 | 大多数场景 |
| nearest | 低 | 最终一致 | 非关键查询 |
我个人习惯是,写操作走 primary,读操作走 nearest。这样写入保证强一致,读取用最近的节点,延迟最低。如果业务要求写后立即读到,那就用 writeConcern: majority 配合 readPreference: primary。
小技巧
MongoDB 4.4 之后支持了 readPreferenceTags,可以按区域标签路由。比如给主区域的节点打上 region: beijing,备区域打上 region: shanghai。应用层可以根据用户所在地,选择最近的节点读取数据。
跨区域复制架构总览
下面这张图展示了三种数据库跨区域复制的核心架构。你可以看到,虽然实现方式不同,但核心思路是一致的:数据在多个区域之间保持同步,任何一个区域故障都不会导致数据丢失。
嗯,到这里,数据库层的跨区复制方案就讲完了。MySQL 的主从复制和 Group Replication、PostgreSQL 的流复制和逻辑复制、MongoDB 的副本集,各有各的适用场景。选型的时候,要考虑业务对一致性、延迟、可用性的要求,没有银弹。