流复制(Streaming Replication)与自动故障转移(Failover)实战:用Patroni或Repmgr搭建生产级数据库集群
更多服务器知识,尽在hostol.com
嘿,各位PostgreSQL的“掌舵人”和数据“守护神”们!咱们都知道,PostgreSQL(简称PG)以其强大的功能、稳定性和开源的特性,赢得了越来越多开发者和企业的青睐。但是,无论PG本身多么牛X,如果它只是孤零零地跑在一台服务器上,那它始终是一个“单点故障”(Single Point of Failure)。想象一下,万一这台服务器硬盘“寿终正寝”、电源“罢工不干”,或者机房网络“抖了一下”,你的核心数据库服务是不是就得跟着“歇菜”?那造成的业务中断和数据风险,简直不敢想!
“难道就没有办法让我的PG数据库像‘打不死的小强’一样坚挺吗?” 你可能会这么问。当然有!答案就是——高可用性(High Availability, HA)!通过构建一个PostgreSQL高可用集群,我们就能在主数据库发生故障时,让备份数据库(副本)迅速“上位”,接管服务,把停机时间缩短到极致,甚至让用户毫无察觉。今天,Hostol就带你深入PostgreSQL高可用的“核心秘境”,从理解基石技术——流复制(Streaming Replication)——开始,再到借助像Patroni或Repmgr这样的“智能指挥官”实现**自动故障转移(Automatic Failover)**,手把手教你搭建一个真正能扛事的“生产级”PostgreSQL数据库集群!
第一章:流复制(Streaming Replication)—— PG高可用的“奠基石”
要想实现高可用,首先得有“替补队员”吧?流复制就是为PostgreSQL培养“替补队员”(副本服务器,Standby/Replica Server)的核心技术。
1. 什么是流复制?—— 主库的“实时数字孪生”
简单来说,流复制就是主数据库(Primary Server)把它的“操作日记”——也就是**预写日志(Write-Ahead Log, WAL)**——几乎实时地、源源不断地“直播”给一个或多个副本服务器。副本服务器收到这些“日记”后,就在自己身上“重演”一遍主库所做的所有数据修改操作,从而使自己的数据状态与主库保持高度一致(或者只有非常微小的延迟)。
这就像是一位“速记员”(副本服务器)在旁边一字不差地记录下“主讲人”(主数据库)说的每一句话(数据变更),并把这些记录整理成册,使得自己手上的“讲稿副本”和主讲人的“原稿”几乎完全一样。
2. 流复制的“两种姿态”:异步 vs. 同步
- 异步复制 (Asynchronous Replication): 这是默认且最常用的模式。主库在提交一个事务(比如
INSERT
,UPDATE
,DELETE
操作)时,它不会等待副本服务器确认收到并应用了对应的WAL日志。主库自己“搞定”就直接告诉客户端“事务成功了”。- 优点: 对主库的写入性能影响最小,因为不用等副本。
- 缺点: 如果主库突然宕机,那么那些已经提交但在主库上还未来得及发送给副本的WAL日志就可能丢失,导致副本数据比主库落后一丁点(通常是毫秒到秒级),这可能造成少量数据丢失。这就是所谓的RPO(Recovery Point Objective,恢复点目标)大于零。
- 同步复制 (Synchronous Replication): 主库在提交事务后,必须等待至少一个(或多个,可配置)“同步副本”确认已经收到(甚至已经应用)了相关的WAL日志后,才会向客户端报告“事务成功”。
- 优点: 能保证在主库宕机时,至少有一个副本拥有所有已提交事务的数据,从而实现RPO为零(无数据丢失)。
- 缺点: 会增加主库写操作的延迟,因为要等副本的确认。如果同步副本出现网络问题或自身故障,主库的写入也可能被阻塞。
选择哪种模式,取决于你对数据一致性和写入性能的权衡。大多数场景下,异步复制配合良好的监控和快速的故障转移,已经能满足需求。
3. 配置流复制的关键“开关”(postgresql.conf
, pg_hba.conf
, recovery.conf
/standby.signal
)
搭建流复制,主要涉及修改以下几个地方的配置:
在主数据库服务器上:
postgresql.conf
:wal_level = replica
(或更高,如logical
):必须设置为这个级别或更高,PG才会产生足够的WAL信息供复制使用。max_wal_senders = 10
(示例值):允许同时连接到主库进行WAL流式传输的副本数量(或WAL发送进程数量)。wal_keep_segments = 64
(示例值,老版本PG):在pg_wal
目录(老版本是pg_xlog
)中保留的旧WAL段文件的最小数量。如果副本落后太多,主库又清掉了它需要的WAL段,复制就会中断。 (注意: 新版本PG更推荐使用下面的“复制槽”)archive_mode = on
和archive_command = '...'
:强烈建议同时配置WAL归档。这样即使wal_keep_segments
不够用,或者副本长时间离线,它也能从归档位置获取所需的WAL文件来追赶。
pg_hba.conf
(主机访问控制文件): 需要添加规则,允许副本服务器以特定的复制用户连接到主库的replication
“数据库”。# TYPE DATABASE USER ADDRESS METHOD host replication your_repl_user replica_server_ip/32 md5 # or scram-sha-256
在副本数据库服务器上:
- (对于PG 11及更早版本)创建一个
recovery.conf
文件在数据目录下,内容大致如下:[recovery.conf 内容示例 - 老版本PG] standby_mode = 'on' primary_conninfo = 'host=primary_server_ip port=5432 user=your_repl_user password=your_repl_password application_name=my_standby1' # restore_command = 'cp /path/to/archived_wals/%f "%p"' # 如果配置了WAL归档 # primary_slot_name = 'my_standby1_slot' # 如果使用了复制槽
- (对于PG 12及更新版本)不再使用
recovery.conf
。你需要在数据目录下创建一个空的standby.signal
文件,然后把primary_conninfo
和primary_slot_name
(如果用槽)等参数直接写入postgresql.conf
文件,或者更推荐的是写入由postgresql.auto.conf
管理的配置中(通常通过ALTER SYSTEM
命令设置,或者在初始化副本时由pg_basebackup
工具自动生成)。restore_command
也移到了postgresql.conf
中。
4. “复制槽” (Replication Slots) —— 防止WAL日志“失联”的保险丝
这是一个非常重要的特性!复制槽由副本在主库上创建。只要复制槽存在,主库就不会删除该副本尚未消费的WAL日志,即使副本长时间离线。这比单纯依赖wal_keep_segments
更可靠,能有效防止因副本落后太多而导致的复制中断和需要全量重建副本的麻烦。但也要注意监控复制槽的状态,如果副本永久下线,记得在主库上删除对应的复制槽,否则主库磁盘可能会被旧的WAL日志撑满。
第二章:自动故障转移 —— 当“主讲人”突然“晕倒”时
流复制解决了数据冗余的问题,我们有了“热乎”的副本。但如果主库真的不幸宕机了(比如硬件故障、操作系统崩溃),怎么办?
我们可以手动操作:登录到一台最新的副本服务器,执行pg_ctl promote
(老版本用trigger_file
) 将其提升为新的主库,然后修改所有应用的数据库连接字符串指向这个新主库,再让其他副本也指向这个新主库……天哪,如果这事儿发生在凌晨三点,等你睡眼惺忪地做完这一套,业务可能已经中断了半天了!
所以,我们需要一个“智能调度系统”,能够:
- 自动检测主库是否真的“挂了”。
- 在确认主库故障后,从众多副本中自动选举一个最合适的“继任者”。
- 自动将“继任者”提升为新的主库。
- 自动通知其他副本“换大哥了”,让它们开始从新主库复制数据。
- (理想情况下)还能自动更新应用的数据库连接指向,或者通过某种方式(如VIP漂移、DNS更新)让应用无感知地连到新主库。
这就是“自动故障转移”的魅力!而Patroni和Repmgr就是能帮我们实现这套复杂逻辑的两位“大内总管”。
第三章:认识“大内总管” —— Patroni 与 Repmgr
这两位都是PostgreSQL社区广泛使用的开源HA管理工具。
1. Patroni:云原生时代的“民主选举委员会主席”
- 特点: Python编写,非常灵活和强大。它依赖一个分布式配置存储(DCS),比如etcd、Consul、ZooKeeper,甚至是Kubernetes API,来进行集群成员管理、领导者选举(Leader Election)、配置共享和状态同步。
- 工作方式: Patroni在每个PG节点上运行一个守护进程,这些进程通过DCS进行通信和协调。DCS就像是它们的“联合国安理会”,大家在这里投票选出“带头大哥”(主库)。如果主库失联,其他节点会通过DCS重新选举。Patroni还会动态管理PostgreSQL的配置文件(
postgresql.conf
),确保复制等设置正确。 - 优点: 功能全面,高度可定制(通过回调脚本可以实现非常复杂的故障转移逻辑,如VIP管理、通知等),与Kubernetes等云原生环境集成良好,社区活跃。
- 缺点: 依赖外部DCS,增加了架构的复杂度。配置相对Repmgr可能稍复杂一些。
- 把它想象成: 一个由各成员节点组成的“议会”,通过一个高度可靠的“中央选举系统”(DCS)来民主选举和罢免“总统”(主库),并自动处理权力交接。
2. Repmgr:传统运维的“忠诚卫队长”
- 特点: C语言编写(守护进程
repmgrd
)配合SQL脚本,更侧重于PostgreSQL自身的管理。它在每个PG实例中创建自己的元数据表来存储集群信息。 - 工作方式:
repmgrd
守护进程在每个节点上运行,监控本地PG实例和主库的状态。当主库故障时,它会根据配置(比如是否有见证服务器Witness Server来帮助判断、选举策略等)来决定是否执行故障转移,并协调副本的提升和重新指向。 - 优点: 相对Patroni来说,不强制依赖外部的DCS(虽然推荐使用见证服务器来避免脑裂),对于一些不想引入额外组件的简单环境,可能更容易上手。有清晰的命令行工具进行管理。
- 缺点: 在某些复杂场景下的灵活性和自动化程度可能不如Patroni。对PostgreSQL版本的依赖可能更紧密一些。
- 把它想象成: 一支经验丰富、纪律严明的“皇家卫队”,当“国王”(主库)出意外时,他们会按照既定的“王位继承法”(配置和选举逻辑)迅速拥立“王储”(最优副本)登基。
如何选择?
- 如果你追求与Kubernetes等云原生生态的深度集成,需要非常灵活的定制能力,并且不介意引入etcd/Consul等DCS组件,那么Patroni可能是更好的选择。
- 如果你的环境相对简单,或者你更倾向于一个轻量级、对外部依赖较少的方案,并且主要依赖PostgreSQL自身机制,那么Repmgr可能更适合你。
(由于篇幅限制,我们下面以**Patroni配合etcd**的思路,简要介绍一下搭建HA集群的核心步骤和考量,Repmgr的思路有共通之处,但具体命令和配置会有所不同。)
第四章:实战演练 —— 用Patroni打造“打不死”的PG集群(概念流程)
搭建一个完整的生产级Patroni集群是个细致活,这里我们主要梳理核心步骤和思想。
假设环境: 你有至少三台服务器(一台做主,一台做备,一台可以做纯etcd节点,或者也跑PG作为第三个副本兼顾仲裁)。一个etcd集群(通常至少3个节点保证高可用)。
- 基础准备:所有节点安装PostgreSQL和Patroni 确保所有预期的PG节点上都安装了相同大版本的PostgreSQL,以及Patroni和它所依赖的Python库(如
psycopg2-binary
,python-etcd
等)。 - 配置etcd集群(如果还没有的话) Patroni需要一个稳定运行的etcd集群(或Consul/ZooKeeper)来存储集群状态和进行领导者选举。etcd自身的HA部署也很重要。
- 为每个PG节点编写Patroni配置文件 (
patroni.yml
) 这是Patroni的核心。每个节点上的patroni.yml
内容大部分相同,但会有一些节点特定的配置(比如节点名)。关键配置项包括:# patroni.yml 示例片段 scope: my_pg_cluster # 集群名称,所有节点必须一致 namespace: /service/ # 在DCS中存储此集群信息的基础路径 # PostgreSQL相关配置 (Patroni会用这些信息来初始化和管理PG) bootstrap: dcs: postgresql: use_pg_rewind: true # 推荐开启,方便旧主库恢复后快速作为新副本加入 # ...其他bootstrap参数... # initdb的参数,或者pg_basebackup的参数等 # Patroni会负责初始化第一个节点(主库),其他节点会自动从主库做pg_basebackup # DCS连接信息 (以etcd为例) etcd: hosts: etcd1_ip:2379,etcd2_ip:2379,etcd3_ip:2379 # 你的etcd集群地址 # ttl: 30 # loop_wait: 10 # PostgreSQL本地配置 postgresql: listen: '0.0.0.0:5432' # PG监听的地址和端口 connect_address: 'this_node_ip:5432' # 本节点对外的连接地址 data_dir: /var/lib/postgresql/14/main # PG数据目录 # Patroni会管理postgresql.conf和pg_hba.conf的部分内容 # 你可以在这里指定一些你想让Patroni设置的PG参数 parameters: max_connections: '100' # ... 其他PG参数 ... authentication: replication: username: your_repl_user password: your_repl_password # 实际生产中用更安全的方式管理密码 # ... 其他用户的认证规则 ... # REST API配置 (Patroni自身提供API供管理和监控) restapi: listen: '0.0.0.0:8008' connect_address: 'this_node_ip:8008' # ...可选的认证配置... # 回调脚本 (可选,用于在特定事件发生时执行自定义操作,如VIP漂移、发通知等) # watchdog: # mode: automatic # device: /dev/watchdog # safety_margin: 5
- 启动Patroni服务 在所有节点上,用类似
patroni /path/to/patroni.yml
的方式启动Patroni服务(通常会把它配置成系统服务,如systemd unit)。Patroni启动后,会连接到DCS,进行领导者选举。第一个成功初始化(或已存在主库)的集群,会有一个节点成为主(Leader),其他节点会自动配置为从该主库进行流复制的副本(Replica)。 - 监控集群状态 可以使用
patronictl -c /path/to/patroni.yml list
命令查看集群状态,哪个是主,哪些是备,复制延迟等。 - 模拟与测试故障转移! 这是**最最最重要**的一步!搭建好集群不是结束,而是开始!你必须反复测试各种故障场景:
- 手动停止主库的PostgreSQL服务:
sudo systemctl stop postgresql
。观察Patroni是否能自动将一个副本提升为新主。 - 模拟主库网络故障:用防火墙断开主库与DCS及副本的网络连接。
- 测试手动切换(Switchover):使用
patronictl switchover
命令进行计划内的主备切换。
- 手动停止主库的PostgreSQL服务:
应用如何连接到“永远的主库”?
既然主库可能随时“漂移”,应用总不能写死一个IP吧?常用方案有:
- VIP (虚拟IP) + Keepalived/Corosync+Pacemaker: Patroni可以通过回调脚本,在主库上激活一个VIP,在故障转移后将VIP漂移到新主库上。应用始终连接这个VIP。
- DNS更新: 通过Patroni回调脚本,在故障转移后更新内部DNS记录,让应用通过一个固定的域名连接。但DNS缓存可能导致切换不及时。
- 负载均衡器/连接代理: 在应用和PG集群之间放一个像HAProxy、PgBouncer(配置为会话模式并能感知主库变化)这样的中间件。它们负责探测当前哪个是主库,并将连接路由过去。Patroni的REST API可以帮助这些中间件获取当前主库信息。
- Kubernetes环境: 如果PG跑在K8s里,Patroni可以更新K8s的Service和Endpoints,让应用通过Service名访问主库。
生产级HA集群的“必修课”:
- 数据一致性与复制模式: 仔细权衡异步与同步复制对你的业务RPO和性能的影响。Patroni支持配置同步复制。
- 网络延迟是天敌: 主库、副本、DCS节点之间的网络必须低延迟、高可靠。跨地域部署时尤其要注意。
- 备份策略不能丢: HA集群主要解决的是服务可用性问题,不是数据备份问题!你仍然需要定期对数据库进行物理备份(如
pg_basebackup
)和逻辑备份(pg_dump
),并确保备份在异地存储。 - 监控!监控!监控! 不仅要监控PostgreSQL本身,还要监控Patroni服务状态、DCS集群健康、复制延迟、磁盘空间、VIP状态等等。
- 理解“脑裂”(Split-Brain)并避免它: 当网络分区导致集群成员无法互相通信时,可能出现多个节点都以为自己是主库的情况,这就是“脑裂”,非常危险。DCS(如etcd的Raft协议)和Patroni的领导者选举机制,以及repmgr的见证服务器和仲裁逻辑,都是为了最大限度地避免这种情况。确保你的DCS集群本身是高可用的!
- 定期演练: 不要等真的出事了才发现HA配置有问题。定期进行故障转移演练,就像消防演习一样,能让你在真正灾难来临时从容应对。
呼~PostgreSQL的高可用集群,是不是感觉像是在构建一个精密的“星际舰队”?从流复制这个“引擎核心”,到Patroni/Repmgr这些“智能导航与指挥系统”,再到VIP、负载均衡这些“对接港口”,每一个环节都至关重要。搭建和维护一套生产级的PG高可用集群,确实是对运维人员技术和经验的极大考验。但一旦成功,它为你带来的那种“数据永不眠,服务永不歇”的安心感,绝对是无与伦比的!Hostol希望这篇“高可用进阶指南”能为你点亮通往更稳定、更可靠数据库架构的道路。记住,理论与实践相结合,不断测试与优化,才能真正驾驭这头强大的“大象”!