news 2026/10/17 12:41:09

PostgreSQL高可用架构:主从复制、自动切换与读写分离实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostgreSQL高可用架构:主从复制、自动切换与读写分离实战

1. 从单点到高可用:为什么需要这套架构

先讲一个我实际经历的场景。某公司有一套核心业务系统,早期数据库就一台主库,数据量上来之后,备份、恢复、报表查询都挤在这一个实例上。某天凌晨磁盘告警,主库所在机器硬件故障,整个业务直接停摆。那天夜里最痛苦的不是修复机器,而是发现没有一套现成的切换预案——备库倒是配了,但从来没有演练过切换流程,真到了紧急关头没人敢动。

这件事之后,我开始认真梳理 PostgreSQL 的高可用和读写扩展方案。主从复制解决的是“数据冗余 + 故障切换”的问题,负载均衡解决的是“查询压力分摊 + 连接合理路由”的问题。这两块组合在一起,就是一套比较完整的高可用架构。

这套方案适合谁?适合数据库已经有一定规模、业务对连续性有要求、或者正在从单实例向集群演进的团队。运维人员、DBA、后端开发,只要负责 PostgreSQL 的稳定运行,这篇文章都值得看。

我下面分享的内容,都是基于 PostgreSQL 流复制(Streaming Replication)这套成熟机制展开的。切换工具我用 Patroni 和 repmgr 做过对比,负载均衡层我用 Pgpool-II 和 HAProxy 都实测过。每个方案我都会讲清楚选型理由、部署要点,以及我踩过的坑。

2. 主从复制:从 WAL 说起

2.1 流复制的底层机制

PostgreSQL 的主从复制,核心机制是基于 WAL(Write-Ahead Logging,预写日志)。主库在写入数据页之前,先把变更记录写入 WAL 日志,从库通过网络持续拉取这些 WAL 记录,并回放到本地,从而保持和主库的数据一致。

流复制(Streaming Replication)是这套机制里最常用的实现方式。从库启动后,通过 walreceiver 进程主动连接到主库的 walsender 进程,主库把 WAL 记录实时推送给从库,而不是等 WAL 文件写满后再整体传过去。这个推送是持续不断的,所以正常情况下主从之间的数据延迟非常低,通常只有几秒甚至毫秒级。

这里有个关键概念叫 LSN(Log Sequence Number,日志序号),它是 WAL 日志的定位标记。主从之间的同步状态,本质上就是看两边的最新 LSN 差了多少。从库的回放进度追赶上主库的写入进度之后,就会进入“流式复制”状态。

配置流复制有几个关键参数必须理解:

  • wal_level = replica(或更高),决定 WAL 里记录的信息量。默认的 minimal 级别不包含足够的信息用于复制,必须调整。
  • max_wal_senders:允许同时多少个 WAL 发送进程,也就是能连接多少个从库。每增加一个从库,至少要加一个。
  • max_replication_slots:复制槽的数量。如果要做流复制,建议开启复制槽,防止主库 WAL 日志还没被从库拉走就被回收,导致从库无法续传。
  • hot_standby = on:从库上开启只读查询能力,这样才有意义做读写分离。

2.2 同步复制与异步复制的取舍

这是选型里最重要的一个决策点。异步复制下,主库提交事务不需要等待从库确认,性能几乎无损,但主库一旦崩溃,可能有少量数据丢失(取决于延迟多少)。同步复制下,主库提交事务至少要等一个从库写入并确认,确保不丢数据,但写入延迟会显著增加,而且如果从库挂了,主库写入也会被阻塞。

实际业务里怎么选?我给的判断标准很简单:看你的业务能不能容忍丢失最近几秒的数据。

  • 核心交易类、订单类、账户类,建议用 synchronous_commit = remote_apply 或 remote_write,至少有同步保护。
  • 日志类、统计类、内容类,异步就够了,性能优先。

PostgreSQL 的同步复制可以通过 synchronous_standby_names 参数指定,支持 first、any 等不同模式。比如配置任意一个从库确认即可:

synchronous_standby_names = 'ANY 1 (standby1, standby2)'

这意味着主库的提交要等到多个从库中任何一个完成确认。这套机制从 9.6 开始逐渐完善,到 10+ 版本已经比较成熟。

但同步复制有一个需要特别注意的副作用:如果同步从库宕机,主库写入会卡住,直到超时。所以通常不会把所有从库都设为同步,而是保留一个异步从库兜底,或者用 quorum 机制。

2.3 从库搭建的完整步骤

从零开始搭一个流复制从库,大致分几大步。假设主库已经正常运行,我们在新机器上初始化一个从库实例。

第一步,准备一个基础备份。用 pg_basebackup 工具,从主库拉一个一致性快照:

pg_basebackup -h 主库IP -p 5432 -U replicator -D /data/pgdata -X stream -P

这里 -X stream 表示同时开始流式传输 WAL 日志,-U replicator 是专门用来做复制的账号,权限只授予 REPLICATION 即可,不要给超级权限。

第二步,在从库的 postgresql.conf 里配置复制参数。

primary_conninfo = 'host=主库IP port=5432 user=replicator application_name=standby1'

primary_conninfo 是从库连接主库的关键,主库侧会看到来自 replicator 用户的连接,所以要确保 pg_hba.conf 里允许这个账号从从库 IP 发起复制连接:

host replication replicator 从库IP/32 md5

第三步,启动从库。如果是 PostgreSQL 12 及以上,直接在从库 data 目录下创建一个空的 standby.signal 文件,表示该实例以只读热备模式启动:

touch /data/pgdata/standby.signal

然后启动 PostgreSQL。之后通过以下 SQL 验证复制状态:

-- 主库侧执行 select * from pg_stat_replication; -- 从库侧执行 select pg_is_in_recovery();

看到 pg_stat_replication 里有记录,且 state 为 streaming,说明复制链路已经建立。此时可以在从库上跑只读查询,这就是热备(Hot Standby)。

我个人强烈建议第一次搭建时,用 pg_basebackup 而不是手工拷贝数据文件。手工拷贝很容易出现数据目录不一致的问题,pg_basebackup 本身会做校验和状态检查,安全很多。

3. 故障切换:从手动到自动

3.1 手动切换的流程和风险

复制搭好之后,遇到主库故障,最简单直接的做法是手动把从库提升为新主库。PostgreSQL 从 12 开始支持 pg_promote() 函数,也可以在从库上执行:

pg_ctl promote -D /data/pgdata

执行 promote 之后,从库会停止恢复模式,开始接受写入,并从本地 WAL 里生成一个新的时间线(timeline),以便和旧主库区分。

手动切换最大的问题不是命令本身,而是切换决策。什么时候切?怎么确认旧主库真的挂了?如果是网络分区而不是硬件故障,旧主库还在运行但和从库失联,此时强行提升从库,可能有两个实例同时接受写入,也就是脑裂。脑裂发生后,数据一致性恢复非常困难,远比停机更难处理。

所以即使是手动切换,也必须要求从库已经将主库的 WAL 全部拉取并回放完毕,或者确认旧主库确实无法提供服务。否则,切换后会出现“新主库比旧主库少了最后几条变更”的情况,业务数据出现回滚。

手动切换适合什么场景?适合应对可预期的维护窗口,比如硬件更换、内核升级。适合预案清晰、切换频率极低的小团队。但对于“半夜报警、5 分钟内必须恢复”的业务,手动的速度太慢了。

3.2 Patroni 自动切换的原理

自动切换,业界主流方案是 Patroni。它做三件事:通过分布式锁选主、监控所有节点健康状态、在主节点故障时自动提升备用节点。

Patroni 的核心是基于分布式一致性存储(比如 etcd 或 ZooKeeper)来做选主。所有 PostgreSQL 节点都会向 etcd 注册自己的信息,并争夺 leader 角色。正常状态下,只有一个节点持有 leader 锁,其他节点都是从库。当 leader 节点失联一段时间,剩余节点重新选举,其中一个从库会拿到锁并执行 promote。

这套机制好在哪?它从根本上避免了脑裂问题——因为所有节点都以分布式锁为准,即使网络分隔,同一时刻也只会有一个节点认为自己是主库。虽然极端情况下仍然有旧主库短暂“以为”自己还是主的情况,但 Patroni 会在旧主库恢复后将其降级为从库,并重新拉取新主库的数据。

Patroni 的使用需要用一个额外的分布式存储组件,这也意味着架构里多了一个运维对象。对很多团队来说,etcd 本身要维护 3 个节点的高可用,这本身就是成本。但相比 PostgreSQL 直接停摆,这点成本是可接受的。

3.3 我用 repmgr 和 Patroni 的实测对比

我先后在测试环境里分别用 repmgr 和 Patroni 搭建过两套主从架构,做了一些切换演练对比。

repmgr 是一个相对轻量的工具,它也是基于复制槽和 WAL 管理的,提供 repmgr standby promote、repmgr standby follow 这样的命令。它的优点是配置简单,依赖少,没有 etcd 也能跑。但它对于脑裂的防护依赖额外的判断机制,且回切(failback)的流程相对繁琐。

Patroni 的优点是把“选主”和“配置管理”结合起来,切换时不仅提升从库,还会自动更新所有节点的配置信息,让应用层能动态感知新主库的地址。比如配合 HAProxy 或者 Patroni 自带的 REST API,应用无需改连接串即可继续工作。

我实测下来,repmgr 适合中小规模、架构尽量简单的场景;Patroni 适合对自动切换要求高、节点比较多(比如一主多从)的场景。如果你只在两台机器上做一主一从,用 repmgr 就够了;三台以上、还要自动拉起故障实例,上 Patroni 更顺。

3.4 切换过程中的关键注意事项

切换不是敲一条命令那么简单的。有几个细节如果不注意,切换后会出现各种问题。

时间线问题。旧主库恢复后,如果它不知道自己已经被替代,仍以旧 timeline 对外服务,双写就会出现。PostgreSQL 的 timeline 机制就是为了解决这个问题,新主库 promote 后会开启新的 timeline,旧主库恢复连接时会发现自己落后,然后自动跟随新主库。但如果网络分区期间旧主库一直对外写入,恢复后这些数据无法合并到新主库,这就是脑裂的实际后果。

WAL 归档问题。建议主库开启 archive_mode,同时配置 archive_command 归档到共享存储或对象存储。切换后,新主库要能从同样的归档位置恢复数据,所以归档配置要一致。如果归档只指向旧主库的本地目录,切完新主库就断档了。

复制账号的权限。很多团队只在主库上创建复制账号,从库提升为新主后,应用账号、复制账号都还在(因为数据相同),但密码如果有轮换机制就容易出问题。建议把账号管理放到配置管理工具里,通过 Patroni 或类似工具统一下发。

4. 负载均衡:把查询打到正确的节点上

4.1 主从架构下的负载均衡思路

主从架构搭好之后,如果只是把从库当作备份,是很浪费的。热备节点具备只读能力,正好可以把只读业务(报表、统计、后台任务)分流过来,减轻主库压力。这就是读写分离。

读写分离的流量分发策略,主要有三种:

  • 按 SQL 类型路由:写请求强制走主库,读请求分摊到主库和从库。
  • 按连接路由:某些特定客户端会话固定连接到从库,比如报表服务单独使用从库地址。
  • 按事务状态路由:同一个事务里先写再读,则全部走主库,避免从库数据延迟导致读到过期数据。

这里有个容易被忽略的原则:主从架构下的负载均衡不是简单把流量打散,而是要保证业务能接受“从库读到的数据可能不是最新”的延迟。如果业务对数据强一致,读也要走主库。

我在实际项目中见过一个事故:某个运营后台的列表页,查询被路由到了从库,而刚写入的状态在从库上还没回放完成,用户看到的内容和自己刚操作的记录对不上,反复排查才发现是读写分离导致的“幻读”。这种问题在数据强一致场景下非常难解释清楚。

4.2 Pgpool-II 还是 HAProxy

负载均衡层的工具有两个主流方向:应用层代理(Pgpool-II)和网络层代理(HAProxy)。

Pgpool-II 是专门为 PostgreSQL 设计的,内置了读写分离、连接池、负载均衡、自动故障切换等完整功能。它既能做语句级路由(通过解析 SQL 判断读写),也能做连接池复用。配置项多,学习曲线陡,但功能确实全。缺点是一旦 Pgpool-II 本身出问题,影响面很大,而且它管理的是 PostgreSQL 层面的连接状态,对 ODBC/JDBC 等客户端来说增加了协议转换的复杂度。

HAProxy 是通用网络负载均衡器,不感知 PostgreSQL 协议细节,只能做 TCP 层的四层转发。它把连接转发到后端不同的 PostgreSQL 节点,本身不做 SQL 路由,因此更轻、更稳。缺点是它无法判断某条 SQL 是读还是写,要让读写分离生效,只能通过配置多个前端端口来解决:一个端口只转发到主库,一个端口只转发到从库,应用层自行选择。

我实测下来的结论是:如果团队对 PostgreSQL 比较熟,需要开箱即用的读写分离,Pgpool-II 更贴合。如果团队已经有很多 HAProxy 运维经验,想用一套通用方案管理所有流量,HAProxy 更合适。

另外还有一个思路,客户端自行管理读写分离,比如 JDBC 的 PostgreSQL 插件支持多主机配置,主从不同权重,性能表现也不错。这个方案少了一层代理,但需要应用开发配合改造连接串。

4.3 连接池的配置要点

无论选择哪个代理,连接池都是必配项。PostgreSQL 每个连接都是一个进程,连接数过多时会大量消耗系统资源。连接池的作用是让多个应用连接复用到少数几个 PostgreSQL 连接上,降低连接开销。

Pgpool-II 的连接池参数里,关键的是 max_pool 和 num_init_children。num_init_children 控制 Pgpool-II 提前为每个后端 PostgreSQL 实例建立的连接数,max_pool 控制每个应用连接可以复用多少个后端连接。这两个参数需要根据应用并发和数据库 max_connections 一起规划,否则容易出现“连接池把数据库连接数打满,但应用侧还在排队”的情况。

如果是 HAProxy,它本身不带 PostgreSQL 连接池功能,只负责 TCP 转发。要复用连接,还得在 PostgreSQL 前再叠加 PgBouncer 这类专用连接池。这也是我后来更倾向 Pgpool-II 的原因——简化架构层级,少一个组件就少一份故障点。

4.4 基于延迟感知的读写分离优化

简单的读写分离只做分流,但要做得更精细,必须考虑复制延迟。

PostgreSQL 提供了一些系统函数,比如 pg_stat_replication 里的 replay_lsn,可以实时看到从库的回放进度。如果要实现“读请求可能延迟,但不超过阈值”的剥离策略,可以在应用层或代理层做延迟判断:当从库的 replay_lsn 落后主库超过某个阈值,就把这个从库从读流量池里摘除,等追平后再加回来。

实际的实现方式有几种:

  • 在应用层写一个健康检查脚本,周期检查各从库的回放延迟,延迟过高时更新路由配置。
  • 在 Pgpool-II 上配置 health_check,结合延迟指标做节点健康判断。
  • 如果使用 Patroni,它会在 REST API 中暴露每个节点的角色和数据状态,应用层可以直接查询。

这个优化对高并发读的业务很关键。比如电商大促期间的库存查询、榜单刷新这类读多写少的场景,只要从库延迟超过几秒,用户看到的库存数据就明显不准,投诉就来了。

我在一个模拟项目中做过这套延迟感知的方案。健康检查模块每 5 秒拉取一次各从库的 pg_stat_replication,如果发现某个从库延迟超过 3 秒,就暂时把它从读流量池标记为不可用;等延迟回到 1 秒以内再接回来。这样业务读流量始终集中在可接受延迟的节点上,主库负载也明显下降。

5. 架构落地:一主双从 + 读写分离的完整实践

5.1 整体拓扑与组件规划

我在自己负责的模拟项目 X 中,落地过一套“一主双从 + Pgpool-II 读写分离 + repmgr 自动切换”的架构。选择这个组合的考虑是:三台数据库服务器保证冗余,Pgpool-II 承接应用侧连接,repmgr 负责自动切换,不需要引入额外的分布式存储,整体运维成本可控。

拓扑大致如下:

  • 节点 A:主库,承担写入和少量强制读
  • 节点 B:从库 1,异步复制
  • 节点 C:从库 2,异步复制,并作为切换候补
  • Pgpool-II 节点:承接应用连接,做读写分离和连接池

三台数据库节点都配置了相同的数据目录结构和配置模板,方便切换后快速调整。归档日志统一放到共享存储上,同时启动参数保证从库可以读取共享存储的 WAL 归档来追平数据。

5.2 Pgpool-II 的读写分离配置实操

以 Pgpool-II 4.x 为例,关键配置文件是 pgpool.conf。先开启读写分离功能:

load_balance_mode = on master_slave_mode = on master_slave_sub_mode = 'stream'

后端节点配置用 backend_hostname、backend_port、backend_weight 等参数,可以指定主库、从库 IP 和权重,权重决定读请求按什么比例分发到各从库。我通常把主库权重设为 0,从库 1 和从库 2 各设 1,这样所有读流量都走从库,主库只处理写入。

注意,Pgpool-II 判断一条 SQL 是读还是写,核心依据是 SQL 的语法解析结果和事务状态。如果在一个未结束的事务里执行了写操作,该事务后续的所有 SQL 都会强制转发到主库,这是 Pgpool-II 的 keep_majority 特性,保证一致性。

配置完成后,用 pcp_attach_node 和 pcp_detach_node 命令可以动态调整节点状态,比如某个从库延迟过高时摘除它,恢复后再挂回来。健康检查参数 health_check_period 设为 10,表示每 10 秒检查一次后端节点可用性。

5.3 repmgr 的自动切换配置

repmgr 配置相对简洁。在每个节点上安装 repmgr 之后,在 repmgr.conf 里指定节点 ID、连接信息、数据目录、归档目录等。

node_id=1 node_name=node-a conninfo='host=node-a port=5432 user=repmgr dbname=repmgr' data_directory='/data/pgdata'

然后用 repmgr -f repmgr.conf primary register 在主库上注册主节点信息,再在从库上执行 repmgr -f repmgr.conf standby clone 拉取数据并注册为备节点。

repmgr 的自动切换需要配置一个事件通知脚本,当它检测到主库不可达时,会根据优先级从候选从库中选一个提升:

repmgr standby promote -f /etc/repmgr.conf

repmgr 支持 witness 节点(见证节点)来缓解脑裂场景。witness 节点不承载数据,但它参与投票,当主库和从库之间的网络发生分区时,witness 能帮助判断哪个分区仍然具备多数节点,从而决定是否切换。

5.4 应用连接的配置方法

应用侧连接不能只写一个数据库地址,必须配置高可用连接串。以 Java 应用为例,使用 PostgreSQL JDBC 的多主机 URL:

jdbc:postgresql://host1:5432,host2:5432,host3:5432/mydb?targetServerType=primary

targetServerType=primary 表示应用只连接主节点,适合写流量。如果要把读流量分散到多个节点,可以配置 targetServerType=any 或 preferSecondary。

如果应用使用 Pgpool-II,连接串就写 Pgpool-II 的地址,由它做读写分离。这里有个经验:不要在企业级应用中同时叠加“应用自建HA + Pgpool-II 负载均衡”两层路由,会导致故障转移时应用连接串无法确定是走了哪一层,排查起来非常痛苦。选一层做主路由,另一层做兜底就行。

5.5 切换演练的完整流程

架构落地后,切换演练是必须的,而且不要只做一次。我的演练流程大致分几个阶段:

  1. 停掉主库的 PostgreSQL 服务(或者模拟网络隔离,直接把主库的网卡禁用)。
  2. 观察 repmgr 是否识别到主库失联,以及是否触发 standby promote。
  3. 确认新主库对外可写,且数据不丢。这一步会对比切换前后主库 WAL 的 LSN 和业务侧事务数。
  4. 恢复旧主库,以从库身份重新加入集群。
  5. 验证旧主库上的数据是否能通过 repmgr standby follow 自动重新同步到新主库,且没有出现双写。

演练中我遇到过最典型的问题是:切换成功,但应用连接池里的旧连接没有重连到新主库,导致后续所有写操作都失败。根本原因在于连接池的 keepalive 时间过长,JDBC 连接池默认没有做自动重连。

解决方案是调整应用连接池配置:测试连接可用性的间隔调短(比如 5 秒),连接空闲超时调短(比如 60 秒),并且在 JDBC 连接串里增加 connectTimeout 和 socketTimeout。另外,在故障切换时手动触发连接池的清理和重建,也是避免业务长时间中断的有效手段。

6. 常见问题与排查技巧实录

6.1 从库一直显示 streaming 但数据不更新

现象:pg_stat_replication 里 state 是 streaming,但从库执行查询却看不到主库刚写入的数据。

大概率是回放进度卡住了。先在从库上执行:

select pg_last_wal_replay_lsn(); select pg_last_wal_receive_lsn();

如果 receive_lsn 一直增长而 replay_lsn 不动,说明 WAL 已经收到但回放进程阻塞。常见原因是从库上有长时间运行的长事务,或者某个空闲事务持有了旧快照,导致 vacuum 和回放都无法推进。

排查方法:查看 pg_stat_activity,找到 state 为 idle in transaction 的进程,杀掉或等待其结束。另外检查 hot_standby_feedback 参数,如果是 on,主库会避免回收从库可能还用到的旧版本数据,但也会导致主库膨胀,需要权衡。

6.2 主库宕机后,从库自动提升失败

repmgr 或 Patroni 配置了自动切换,但主库挂掉后,从库并没有自动提升。常见原因:

  • 候选从库数量不足。repmgr 默认要求至少一个可见的候选从库才能切换,如果只有一个从库且它自己也失联,无法切换。
  • witness 节点配置错误。witness 节点需要和主库、从库网络互通,但某些云环境下安全组没放行,导致投票失败。
  • repmgr 事件通知脚本执行出错,promote 命令没有真正执行。

排查时,先看 repmgr 日志,确认是否有 “standby promote” 的执行记录,再看数据库日志里是否有 promote 相关的错误。顺手还要检查 etcd/分布式锁的状态,如果是 Patroni,直接查 etcd 中 leader key 的归属。

6.3 负载均衡后端节点健康检查误判

Pgpool-II 配置了 health check,但偶尔会出现“明明数据库正常,却被标记为 down”的情况。这种误判大多数不是数据库真的挂了,而是健康检查的连接配置没写好。

Pgpool-II 的 health check 通过向数据库发起一个简单查询来判断节点状态,如果查询超时(比如一次 full vacuum 锁竞争导致响应变长),就判定节点故障。解决方案是调大 health_check_timeout,并且把 health_check_user 的权限设置为可查询系统视图,不要使用超级用户。

另外,健康检查不要只依赖 TCP 端口连通,要配置成查询模式,比如:

health_check_query = 'SELECT 1'

这样能真正感知 PostgreSQL 的响应,而不是网卡通就以为活。

6.4 复制延迟长期偏高

复制延迟 = 主库的写入进度 − 从库的回放进度。如果延迟长期偏高,先分清是网络传输慢还是回放慢。

网络传输慢,一般体现在 pg_stat_replication 的 replay_lsn 和 sent_lsn 差距较大,说明从库没及时收到 WAL。此时检查主库的 max_wal_senders、wal_keep_segments 或复制槽设置,如果 WAL 被回收导致从库追不上,就要用 pg_replslot 扩展一下复制槽保留策略。

回放慢,主要看从库的磁盘 IOPS 和 CPU。回放是单进程串行的,如果从库还在跑大量读查询,IO 竞争激烈,回放进度会被拖慢。优化方向要么给从库更好的 IO 能力,要么减少从库上的读负载,必要时可以增加从库节点分摊读压力。

6.5 切换后旧主库重新加入集群,失败

旧主库恢复后重新加入集群,最常见的问题是 timeline 不一致。旧主库在故障期间可能已经产生了新的 WAL 记录,当它以从库身份重新连新主库时,PostgreSQL 会尝试从新主库拉取数据,但本地 WAL 时间线和主库不一致,导致回放失败。

解决办法:把旧主库的数据目录清空,重新用 pg_basebackup 或者 repmgr 的 clone 功能从新主库拉取全量数据。如果你不想丢失旧主库在故障期间产生的 WAL,也可以尝试用 pg_rewind 工具,它会基于新主库的时间线,把旧主库的差异数据追平。

pg_rewind 的操作要非常小心,它只支持在旧的物理备节点上执行,且要求旧主库配置了 wal_log_hints 或 checksums。执行前一定确认旧主库的数据不能被业务写入,否则会把新主库也带坏。

6.6 问题速查表

症状可能原因排查命令/思路
从库不追数据长事务阻塞回放查看 pg_stat_activity 中 idle in transaction 进程
主库宕机不自动切换候选从库不足/witness 配置错误检查 repmgr 日志、repmgr cluster show
健康检查误判超时时间过短/查询权限不足调大 health_check_timeout,改为 SELECT 1 查询
复制延迟高WAL 回收、IO 瓶颈、读查询压力大对比 sent_lsn 和 replay_lsn,查 IO 指标
旧主库无法回归timeline 不一致用 pg_rewind 或全量 repmgr clust 重建
应用写入失败连接池未自动重连新主库调整连接池 keepalive,清理空闲连接

7. 一些值得再想一层的细节

主从复制和负载均衡这套架构,做到能跑并不难,难的是把边界想清楚。

一个容易被忽略的点是监控。没有监控的主从架构等于没有安全网。我推荐至少采集以下指标:主从之间的复制延迟、WAL 生成速率、从库回放速率、复制槽状态、连接数使用率、健康检查结果。这些指标每隔 5~10 秒采集一次,预警阈值要结合业务对延迟的接受度设定,而不是等出事了才看。

另一个点是备份策略。主从复制不等于备份。如果主库上执行了一条误删除语句,这条语句也会被复制到从库,从库数据同样被删。所以备份仍然是必要环节。建议保留至少一个不在复制拓扑内的独立备份节点,周期性做全量备份和 PITR(时间点恢复)。这个独立的备份节点可以承担一些非实时的报表查询,但一定不要把它当作正常的从库来承载读流量。

还有一个细节是安全。复制账号虽然只需要 REPLICATION 权限,但它能读取 WAL 里的所有数据变更,所以必须严格管理。复制流量最好走内网或者加密通道,不要暴露在公网。pg_hba.conf 里控制复制账号的来源 IP 尽量精确到具体网段,并且定期轮换密码。

我负责的模拟项目 X 在运行过程中,还遇到过一个问题:主库的 max_wal_senders 设置太小,从库数量增加后,新从库一直无法建立连接。排查后发现,历史遗留了多个断开的 walsender 进程没清理,把连接数占满了。这类问题很难靠表面状态发现,一定要配合监控和定期的连接数巡检。


我个人在实际操作中的体会是:架构方案本身并不难,真正决定成败的是细节管理。流复制、切换工具、负载均衡,每块都有成熟的开源方案,但如果没有经过充分的切换演练、没有监控覆盖、没有连接池重连机制,方案部署得再漂亮,故障来临时还是会手忙脚乱。

最后再分享一个小技巧:无论用哪种方案,定期做“故障注入式演练”非常有用。我每个月会在测试环境随机停掉某个数据库节点,然后观察整个集群的自动恢复流程是否符合预期。演练不仅验证了工具配置,还能逼着团队把操作文档、责任分工、应急预案都完善起来。这套架构值不值得上、怎么上最合适,最终还是要回归到你业务的连续性要求来权衡。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 12:40:34

智慧工地大数据一体化平台:从架构选型到落地验收

简介:这份《智慧工地大数据一体化管理平台解决方案》PPTX面向建筑行业信息化从业者、施工项目管理人员及智慧工地方案设计人员,针对工地安全监管被动、人员机械管理难、环保监测不足等痛点,给出了一套涵盖数据采集、智能监控与云平台联动的整…

作者头像 李华
网站建设 2026/10/17 12:39:41

Stable-Baselines3 环境配置实战:从零安装到跑通 PPO 全流程

第一次跑强化学习,最容易被劝退的不是算法公式,而是“装库”。我见过太多人把一整天耗在依赖冲突、版本对不上、各种 import 报错上,最后甚至连 PPO 长什么样都没见到。Baselines3 这个库本身并不难装,真正难的是你不太清楚它背后…

作者头像 李华
网站建设 2026/10/17 12:39:39

YOLOv5+ArcFace人脸检测与识别联合流水线实战

简介:本资源是一个基于YOLOv5与ArcFace双模型协同的人脸识别完整项目实现,面向深度学习初学者、计算机视觉开发者及AI安防应用实践者,解决端到端人脸检测身份识别的一体化建模问题,适用于门禁系统、考勤管理、监控分析等实际场景。…

作者头像 李华
网站建设 2026/10/17 12:39:32

用Agent Skills构建多平台订单自动化工作流实战解析

前阵子帮一位做跨境电商的朋友处理订单流程,他同时在四个平台开了店,每天要反复登录后台、导出订单、手工合并地址、再回填到 ERP 里。搞了一个月,他最大的体会不是单量有多大,而是重复操作能把人磨疯。后来我用 Agent Skills 配合…

作者头像 李华
网站建设 2026/10/17 12:39:24

分阶段奖励塑形+约束多目标强化学习:稳定训练杂技机器人后空翻

“奖励设计得越细,机器人学得越好”——这句话在大部分任务里是成立的,但放到杂技机器人身上,我第一次按这个思路尝试时,得到的不是优美的后空翻,而是一台在仿真环境里疯狂转圈、最后把自己“甩”出去的失控体。后来复…

作者头像 李华