ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从选型到切换:磐维数据库双中心流复制容灾集群搭建全记录

从选型到切换:磐维数据库双中心流复制容灾集群搭建全记录 今年年初我们数据库团队接了一个硬任务把跑在单机房的磐维数据库改成一套双中心容灾的流复制集群。当时方案选型、参数调优、切换演练加在一起差不多干了一个月中间踩了不少坑。这篇文章我把整套搭建过程从头到尾理一遍——为什么选流复制、节点怎么规划、参数怎么配、切换怎么练适合正在做国产数据库容灾改造的DBA和运维同学参考。如果你手里也是类似架构主库在A机房、备库在B机房想做到机房断电不丢数据、业务分钟级恢复那这篇正好能用上。1. 为什么双中心容灾更适合选流复制1.1 先想清楚容灾目标再谈技术选型做容灾之前第一件事不是找工具而是把目标量化。数据库容灾领域两个最基础指标RTO和RPO。RTO是故障发生后业务恢复允许的时间RPO是故障时可容忍丢失的数据量。单机房时代我们靠备份恢复RPO可能是上次备份到故障点之间所有数据小时级RTO取决于备份集大小和恢复人的手速通常半小时往上。双中心容灾要解决的正是这个痛点生产中心彻底挂掉时灾备中心能接住核心交易。目标定多少直接决定架构复杂度。我们的业务对账粒度比较细领导给的预期是RPO接近0也就是主库提交的事务至少不能丢RTO控制在分钟级。明确这个预期之后方案选型就顺理成章了。这里有个常见误区有人觉得只要每晚把数据文件复制到灾备机房也算容灾这其实是备份不是容灾。真正的容灾要求灾备端的数据实时或准实时追平主库并且具备快速接管业务的能力。所以我们在需求评审阶段就明确落地方案必须有持续的数据同步通道而不是定时全量拷贝。1.2 在流复制、逻辑复制、存储复制之间做取舍当时我们重点比对了三条路线各有适应场景。流复制主库把WAL日志通过replication协议实时发给备库备库拿到WAL后重新应用。这是磐维数据库和PostgreSQL生态原生支持的能力数据一致性最接近物理级切换逻辑简单不额外依赖硬件和中间件。逻辑复制把数据变更解析成逻辑记录再传递可以异构、可以只同步部分表但架构复杂DDL处理有诸多限制全库级容灾并不是它的强项。存储复制靠存储阵列在块设备层做镜像数据库无感RPO可以做到极低但成本高出一大截而且切换后还需要数据库一致性处理落地周期长。方案对比数据一致性架构复杂度成本适用场景物理流复制高按WAL顺序回放低数据库原生低同构数据库全库容灾逻辑复制中按SQL级逻辑高需小心DDL中异构同步、部分表同步存储复制高块级一致高依赖存储设备高核心库高级别容灾结论很清楚我们场景就是磐维数据库到磐维数据库没有异构需求没有跨库同步需求流复制是最合适的。它不需要昂贵的存储阵列不需要额外同步中间件而且与备份、归档、只读查询天然配合良好后续运维成本也可控。选型会开了一下午最终落在“主备物理流复制双中心”这个组合上。2. 双中心容灾集群的节点规划与同步策略2.1 中心A、中心B的角色划分与网络规划双中心容灾不是随便找两台服务器把复制配起来就行节点角色和网络链路必须提前设计。我们用的是双机房对称布局中心A是生产中心部署主库节点中心B是灾备中心部署备库节点。两个机房之间用专线互通业务流量和数据库复制流量都走这条链路。网络设计上我给DBA同学一个建议复制流量最好能跟业务流量在逻辑上隔离最理想是数据库节点单独划一个VLAN或走独立路由。原因很直接业务高峰时流量抖动会影响WAL传输备库延迟一旦拉大容灾能力就打了折扣。我们当时给主备之间预留了千兆专线按业务峰值WAL产生速率估算余量在5倍以上这后面会细说。节点规划还有一层容易被忽视仲裁节点。两节点的流复制集群做手动切换没问题但想实现自动故障切换必须有一个独立于两个中心之外的第三方仲裁否则网络分区时就分不清该让谁接管。仲裁节点可以是一台小规格的云主机也可以是一套分布式一致性服务具体方案第5章展开讲。2.2 同步流复制与异步流复制RPO和可用性的权衡流复制本身有两种工作模式同步和异步。这对双中心容灾来说是最关键的一个分岔口。异步模式下主库提交事务不需要等备库确认备库只是尽量追。好处是主库性能完全不受跨机房链路影响坏处是主库宕机瞬间已经提交但还没传到备库的事务会丢。RPO无法归零。如果你的业务能接受几秒到几十秒的数据丢失异步模式最省心。同步模式下主库提交事务要等备库返回确认确认粒度还可以分几档备库收到WAL但没落盘、落盘但没回放、已经回放完成。回放完成的remote_apply一致性最强RPO基本归零但主库每一次写提交都要跟备库做一次“握手”跨机房网络延迟越大主库写入延迟越高。我们最终选了同步模式synchronous_commit设成remote_apply结合同步备库列表限定等待对象。当时部门有同事质疑这会让写入变慢实践证明在专线RTT稳定在5ms的情况下性能损耗在可接受范围。真正危险的不是变慢而是网络抖动时同步备迟迟不确认主库写事务被阻塞——这个坑我后面单开一节讲怎么防御。2.3 提前设计IP、目录和用户避免搭建时手忙脚乱搭建前把这些固定下来后面命令才不会写乱项目中心A生产中心B灾备数据库角色主库备库服务器IP192.168.10.11192.168.20.12数据目录/data/pandb/data/pandb归档目录/archives/pandb/archives/pandb监听端口54325432心跳/仲裁连接192.168.30.13连接192.168.30.13这里我特别强调目录和用户的一致性。磐维数据库安装包一般会自带初始化脚本但核心要养成规范数据目录单独挂盘、归档目录和数据目录分离、数据库运行用户独立。我们统一用pandb用户运行数据库避免之后权限问题排查半天。3. 搭建前的系统层准备与磐维数据库参数初始化3.1 操作系统、时钟同步与文件系统注意事项很多流复制问题不是数据库本身出的而是系统层没准备好。我踩过最典型的坑是服务器时钟漂移备库时间比主库慢了几分钟日志里时间戳对不上排查问题的时候一度以为是主备断连。双中心间直接用NTP或chrony统一时钟源这个动作要加进搭建清单别嫌基础。文件系统层面数据库数据目录所在的磁盘建议用XFS或ext4挂在独立逻辑卷上。为什么不建议根目录直接放数据因为根目录一旦被日志、归档这些内容撑满整个数据库实例都会被拖垮太被动了。我们给数据目录和归档目录各划了独立卷其中归档盘按WAL增长速度预留了双周冗余后面备份脚本会定期清理。操作系统还有几个老生常谈但必须做的关闭透明大页、关闭NUMA潜在干扰、调整vm.swappiness。这些在安装文档里都有但现场经常有人漏。漏一个通常看起来没事但高并发下偶发性能抖动会非常难定位。所有系统调优做完记得用os reboot验证自动拉起确认服务正常再进下一步。3.2 数据库层核心参数清单磐维数据库参数体系与PostgreSQL生态一脉相承我这里给一份我在生产环境实测过的核心参数组合大家执行前先用show命令确认一下版本差异。主库postgresql.conf关键配置wal_level replica max_wal_senders 10 max_replication_slots 10 wal_keep_size 4GB synchronous_commit remote_apply synchronous_standby_names FIRST 1 (standby_b) hot_standby on max_standby_streaming_delay 30s archive_mode on archive_command test ! -f /archives/pandb/%f cp %p /archives/pandb/%f逐个说下为什么这样设wal_level设为replica备库才能拿到完整WAL做物理回放如果要跑逻辑复制得设logical但纯容灾场景replica足够。max_wal_senders决定了主库最多能有多少个walsender进程向外发WAL。直接数一数未来要连主库的备库数量再留出归档、监控连接余量。我们两个中心各一台备库另有可能临时拉一个全量所以给了10。max_replication_slots对应复制槽上限。复制槽的作用后面专门讲先记住它和max_wal_senders最好保持同一数量级。synchronous_commit用remote_apply是相对激进的选择要求备库把WAL回放完成才返回。如果你对性能更敏感可以先用remote_write代价是备库OS崩溃时可能丢已写但未刷盘的数据。synchronous_standby_names用FIRST 1指定同步备库列表里最靠前的一个参与同步确认。我们只有一个备库所以实际就是限定主库只等待standby_b确认。备库这边参数相对简单重点是hot_standby要打开这样灾备中心平时可以挂一些只读查询同时max_standby_streaming_delay别设太短否则备库上有长查询时会不停中断回放。3.3 归档日志备库之外的第二道保险有人会觉得有流复制了归档日志就是多余的。我不这么看。流复制覆盖不了两个场景第一备库因为机房级故障或者误操作废掉时需要全量备份加归档把数据追到一个历史时间点第二生产事故误删数据时备份加归档是唯一能精确恢复到误删前一刻的手段。archive_command里我用的是cp往本地归档目录拷贝注意一点归档目录千万别和数据目录放在同一块盘。不然主库数据盘被写满时归档也同时In trouble恢复手段全废。我们另外在灾备机房还有一份远程归档拷贝代价是加大归档目录空间换来的安全感非常值。归档测试一定要做真实演练光看archive_command返回成功不算完。我见过某套环境归档命令写错了文件路径日志一直报错但数据库服务正常没人注意到直到要做PITR才发现归档没生效。这个教训后面展开说。4. 流复制集群搭建实录主库配置与备库拉起4.1 主库初始化与复制授权主库配置分三步初始化实例、改参数、配访问控制。实例初始化我用的是磐维安装包自带的初始化流程核心和传统PostgreSQL的initdb一致指定数据目录和字符集。初始完成后按第3章的参数清单改postgresql.conf然后编辑pg_hba.conf增加复制用户的访问规则host all pandb_admin 192.168.0.0/16 scram-sha-256 host replication replica_user 192.168.0.0/16 scram-sha-256第一行是日常管理账号访问库第二行是replication专用账号权限只给复制用。复制账号没必要开超级用户最小权限原则CREATE ROLE replica_user LOGIN REPLICATION PASSWORD R3pl_Passwd_2024;REPLICATION权限在PostgreSQL生态里是独立权限不是普通登录权限能替代的。配完后重启主库实例pg_hba.conf变更不像参数一样可以reload一部分稳妥起见直接重启。4.2 用pg_basebackup一键拉起备库备库初始数据不推荐手动去拷贝主库文件首选pg_basebackup它是官方推荐的物理备份工具拉取过程中还会自动建立与主库的流复制连接关系。备库服务器装好磐维数据库软件后执行mkdir -p /data chown pandb:pandb /data pg_basebackup -h 192.168.10.11 -p 5432 -U replica_user \ -D /data/pandb -X stream -P -R -C -S slot_a_to_b参数逐一说-h -p -U指定主库地址、端口和复制账号。-D是备库数据目录。-X stream表示把当前WAL日志以流式方式一并拉取避免初始化过程中主库新产生的WAL没有被包含导致备库启动后接不上。-R会在数据目录生成standby.signal文件并自动写入primary_conninfo连接主库的信息。这是备库身份的关键标志没有它备库会以普通库身份启动。-C -S slot_a_to_b自动创建物理复制槽并且自动写到主库侧省去手工建槽。执行完成后启动备库pg_ctl -D /data/pandb start写到这里要强调pg_basebackup拉取过程中主库会有短暂的额外负载如果你在业务高峰期做初始化务必控制并发。多台备库同时拉的话建议错峰别图省事一次性拉起三台。4.3 复制槽防止WAL被过早回收复制槽这个概念搭建时容易忽略故障时才知道重要。主库生成WAL备库消费WAL正常情况下备库消费多少主库就清理多少。但备库如果停机或者网络断了主库不知道备库已经在落后会继续按保留策略清理旧WAL等备库恢复想要追日志时主库已经把需要的WAL删了后果就是备库报废必须重新全量。复制槽就是为解决这个问题存在的。一个物理复制槽会把主库的WAL保留到该槽对应的消费位点备库还没消费的WAL主库不得清理。刚才-C -S参数已经自动创建了槽可以用下面SQL确认SELECT slot_name, slot_type, active, restart_lsn FROM pg_replication_slots;active字段为t表示备库正在通过该槽消费。有个副作用要提前知道假如备库永久下线这个槽会一直拖着主库WAL不清理时间一长主库磁盘就被撑爆。所以复制槽必须纳入监控发现某个槽长期不active就要排查确认备库不会回来后要及时删槽SELECT pg_drop_replication_slot(slot_a_to_b);4.4 主备同步状态验证集群起来不是终点验证才是。在主库上执行SELECT client_addr, state, sync_state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;正常情况下state是streamingsync_state是sync三个lag字段都接近零或毫秒级。write_lag表示WAL发送到备库的延迟flush_lag是备库落盘延迟replay_lag是备库回放延迟。其中replay_lag最贴近真实数据可见性做容灾演练时主要盯它。在备库上验证身份和进度SELECT pg_is_in_recovery(); -- 返回t说明是备库 SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();两个LSN差距越小越好。还可以做一个直观小实验在主库建一张临时表插入几行马上在备库查如果能立刻看到说明同步链路工作正常。注意备库上表数据可见性受hot_standby回放影响设了remote_apply后主库事务返回成功时备库基本已经可见。5. 容灾切换演练与脑裂防护从计划内切换到故障切换5.1 计划内切换switchover的标准动作双中心集群最核心的价值演练就是计划内切换平时主库在A中心现在要把主库切到B中心比如机房搬迁、硬件维护整个过程要求业务影响最小化。我的标准步骤确认备库同步状态sync_state必须为syncreplay_lag必须为零或极小。通知应用侧把连接切到B中心数据库VIP或读连接串。在A中心主库执行提升备库操作pg_ctl promote -D /data/pandb或者SQLSELECT pg_promote();确认B中心节点已变成主库pg_is_in_recovery()返回f。把A中心原主库转成备库。这里有个关键动作容易被漏ensure原主库作为新备库连接到新主库后要同步修改synchronous_standby_names让新主库等待的备库变成原来的A中心节点否则下一次故障切换就没有同步备库可等了。用第4章的lag查询确认新备库追平切换完成。整个过程听着不复杂但现场演练时我们第一次花了15分钟原因就是应用连接串的切换文档没更新DBA以为切了应用侧配置还指向A中心。现在我们的规约是数据库切换完成后必须由应用团队回一条“连接验证通过”的确认才宣告切换成功。5.2 故障切换与脑裂为什么两个节点不能拍脑袋自动决定谁当家故障切换比计划内切换危险得多核心问题是脑裂。设想一个场景A、B两中心之间专线断了业务在B中心仍然可用运维在B中心把备库提升为主库。此时A中心的主库其实还活着也在接受写入。两边数据同时变化等网络恢复两个中心的数据已经分叉谁都补不回对方整个集群宣告报废。这就是典型的双节点脑裂。两个节点都认为自己该当主谁也不服谁。要防脑裂必须靠一个双方都信任的第三方裁判。我们这次设计用了仲裁节点配合同步确认逻辑。一种做法是把同步提交名单改成quorum方式synchronous_commit remote_apply synchronous_standby_names ANY 2 (node_a, node_b, node_arb)含义是一次事务提交必须得到3个节点中至少2个节点的确认。正常情况下A、B都确认仲裁节点只作陪跑。一旦网络分区发生比如A和仲裁不通A节点无法凑齐2个确认写入会自动阻塞而B节点只要能连上仲裁就有B和仲裁两个确认可以继续提交。这样数据只会在B中心继续增长A中心不会产生新数据等网络恢复A中心完完整整追备库即可。这个机制把“谁该接管”交给法定多数决定从机制上消灭双主。如果不想让每次写事务都等仲裁节点拖慢性能可以只把仲裁节点用于选主判定写入确认仍然只要同步备库一个。生产环境很多团队用Patroni加etcd实现这个逻辑etcd本身要求多数派健康才允许选主本质是同一套思想。5.3 切换后的数据校验和回切路径容灾切换后不能只看数据库起来就宣布恢复。我要求必须做三件校验关键业务表行数对比切换前在主库统计一张核心表行数切换后在备库/新主库重新统计数字一致是最基础的数据存在性验证。业务抽样验证应用侧跑几个典型查询比如按订单号查一笔最近交易确认数据时间戳是新写入的而不是缓存。主备双节点LSN差额复查确认另一个中心正在同步追平且追平时间在可预期范围内。回切比切换更麻烦因为原A中心在故障期间处于停摆或数据落后状态必须等它完全追上并处于同步状态后才建议再切回去。切回时同样走5.1的计划内流程不要觉得从B切回A就是重复一遍所以可以随便操作回切出问题的情况非常多。6. 上线后我踩过的坑WAL堆积、网络抖动与归档联调6.1 备库WAL堆积一场差点撑爆磁盘的故障集群上线第二周我们监控告警主库数据盘使用率突破85%趋势还在涨。排查下来原因是备库触发了一次自动重启重启期间复制槽虽然还在但备库始终没有恢复消费主库侧WAL被复制槽钉住不能清理越积越多。WAL堆积的排查路径是先看复制槽状态SELECT slot_name, active, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag_bytes FROM pg_replication_slots;lag_bytes持续增长就说明备库消费停滞。再结合备库日志看是不是恢复进程有问题。当时备库自动重启后迟迟没拉起来是因为磁盘管理的脚本没把数据目录权限恢复正确pandb用户无法访问数据卷纯属低级失误。修复权限后备库重新启动通过复制槽把积压日志追平主库磁盘占用随之回落。这个坑给我的教训复制槽是保数据的不是保磁盘的两者之间必须有一个磁盘增长监控按槽位lag设置告警而不是等磁盘快满了才发现。6.2 网络抖动对同步复制的影响与降级策略第二坑来自跨机房链路的抖动。某天线上反馈核心交易偶发响应变慢数据库侧看到主库有大量walsender在等待备库确认业务写入被阻塞了几百毫秒到一秒。原因是专线某段路由短暂拥塞RTT从5ms跳到200ms而我们的synchronous_commit是remote_apply每次提交都在等跨中心确认网络一跳业务就跟着被拖住。这个坑的解法不是放弃同步而是要做“可降级”的同步设计。现在我们的处理是分层正常时期保持remote_apply追求RPO归零。监控发现跨中心RTT持续超过阈值或同步备确认超时快速把synchronous_commit调到on也就是备库落盘即返回牺牲一点RPO保命。如果网络长时间不稳定把备库从同步备名单里拿掉改成异步追等网络恢复且备库追平后再加回同步名单。参数调整大多可以reload生效不用重启。这里给一个建议把synchronous_standby_names的设置写成一个运维脚本包含正常、降级、恢复三套配置切换时一键执行避免故障状态下手忙脚乱敲SQL。6.3 归档、全量备份与大数据平台取数的联动最后说一个容易被忽略的联调项归档日志和备份策略要配合。我们当时只做了归档没有配归档清理归档目录在两周内涨了几个GB没感觉但全量备份开始之后归档目录增长更快最终差点把归档盘填满。现在归档清理策略是保留最近7天归档加上最近一次全量备份点之前的归档确认PITR窗口满足后旧归档自动删除。容灾集群稳定运行后业务方自然会拿着更多需求来找你最常见的是“把核心库的表同步到大数据平台做分析”。我的建议是别直接在容灾备库上长期跑重查询备库的角色是接故障不是当分析师。可以让ETL工具从备库导出增量数据到消息队列或者搭一个独立的只读逻辑副本再流入spark、hadoop这类离线集群这样容灾链路和数据加工链路各走各的互不拖累。我们后来就是加了逻辑复制节点单独喂数容灾备库压力一下就下来了。这套集群我运行了小半年最大的体会是流复制本身不难难的是把“RPO是多少、网络抖动怎么办、切换谁说了算”这些问题在设计阶段就想明白。如果你也正在搭建议先把第3章的参数表贴到监控里再逼自己完整走一遍第5章的脑裂演练再考虑上线。那一次演练能暴露的问题比看十遍文档都多。
返回列表