这是本节的多页打印视图。 .
任务教程
- 1: 故障排查
- 2: 误删处理
- 3: 手工 PITR 演练
- 4: 克隆与旁路恢复 PostgreSQL 实例
- 5: 为 PostgreSQL 集群启用 HugePage
- 6: 3坏2应急处理
- 7: 使用 VIP-Manager 为 PostgreSQL 集群配置二层 VIP
- 8: Citus 集群部署
1 - 故障排查
本文档列举了 PostgreSQL 和 Pigsty 中可能出现的故障,以及定位、处理、分析问题的 SOP。
磁盘空间写满
磁盘空间写满是最常见的故障类型。
现象
当数据库所在磁盘空间耗尽时,PostgreSQL 将无法正常工作,可能出现以下现象:数据库日志反复报错"no space left on device"(磁盘空间不足), 新数据无法写入,甚至 PostgreSQL 可能触发 PANIC 强制关闭。
Pigsty 带有 NodeFsSpaceFull 告警规则,当文件系统可用空间不足 10% 时触发告警。 使用监控系统 NODE Instance 面板查阅 FS 指标面板定位问题。
诊断
您也可以登录数据库节点,使用 df -h 查看各挂载盘符使用率,确定哪个分区被写满。
对于数据库节点,重点检查以下目录及其大小,以判断是哪个类别的文件占满了空间:
- 数据目录(
/pg/data/base):存放表和索引的数据文件,大量写入与临时文件需要关注 - WAL 目录(如
pg/data/pg_wal):存放 PG WAL,WAL 堆积/复制槽保留是常见的磁盘写满原因。 - 数据库日志目录(如
pg/log):如果 PG 日志未及时轮转写大量报错写入,也可能占用大量空间。 - 本地备份目录(如
data/backups):使用 pgBackRest 等在本机保存备份时,也有可能撑满磁盘。
如果问题出在 Pigsty 管理节点或监控节点,还需考虑:
- 监控数据:VictoriaMetrics 的时序指标和 VictoriaLogs 日志存储都会占用磁盘,可检查保留策略。
- 对象存储数据:Pigsty 集成的 Silo 对象存储可能会被用于 PG 备份保存。
明确占用空间最大的目录后,可进一步使用 du -sh <目录> 深入查找特定大型文件或子目录。
处理
磁盘写满属于紧急问题,需立即采取措施释放空间并保证数据库继续运行。
当数据盘并未与系统盘区分时,写满磁盘可能导致 Shell 命令无法执行。这种情况下,可以删除 /pg/dummy 占位文件,释放少量应急空间以便 shell 命令恢复正常。
如果数据库由于 pg_wal 写满已经宕机,清理空间后需要重启数据库服务并仔细检查数据完整性。
事务号回卷
PostgreSQL 循环使用 32 位事务 ID (XID),耗尽时会出现"事务号回卷"故障(XID Wraparound)。
现象
第一阶段的典型征兆是 PGSQL Persist - Age Usage 面板年龄饱和度进入警告区域。
数据库日志开始出现:WARNING: database "postgres" must be vacuumed within xxxxxxxx transactions 字样的信息。
若问题持续恶化,PostgreSQL 会进入保护模式:当剩余事务 ID 不到约100万时数据库切换为只读模式;达到上限约21亿(2^31)时则拒绝任何新事务并迫使服务器停机以避免数据错误。
诊断
PostgreSQL 与 Pigsty 默认启用自动垃圾回收(AutoVacuum),因此此类故障出现通常有更深层次的根因。 常见的原因包括:超长事务(SAGE),Autovacuum 配置失当,复制槽阻塞,资源不足,存储引擎/扩展 BUG,磁盘坏块。
首先定位年龄最大的数据库,然后可通过 Pigsty PGCAT Database - Tables 面板来确认表的年龄分布。 同时查阅数据库错误日志,通常可以找到定位根因的线索。
处理
- 立即冻结老事务:如果数据库尚未进入只读保护状态,立刻对受影响的库执行一次手动 VACUUM FREEZE。可以从老化最严重的表开始逐个冻结,而不是整库一起做,以加快效果。使用超级用户连接数据库,针对识别出的
relfrozenxid最大的表运行VACUUM FREEZE 表名;,优先冻结那些 XID 年龄最大的表元组。这样可以迅速回收大量事务 ID 空间。 - 单用户模式救援:如果数据库已经拒绝写入或宕机保护,此时需要启动数据库到单用户模式执行冻结操作。在单用户模式下运行
VACUUM FREEZE database_name;对整个数据库进行冻结清理。完成后再以多用户模式重启数据库。这样做可以解除回卷锁定,让数据库重新可写。需要注意在单用户模式下操作要非常谨慎,并确保有足够的事务 ID 余量完成冻结。 - 备用节点接管:在某些复杂场景(例如遭遇硬件问题导致 vacuum 无法完成),可考虑提升集群中的只读备节点为主,以获取一个相对干净的环境来处理冻结。例如主库因坏块导致无法 vacuum,此时可以手动 Failover 提升备库为新的主库,再对其进行紧急 vacuum freeze。确保新主库已冻结老事务后,再将负载切回来。
连接耗尽
PostgreSQL 有一个最大连接数配置 (max_connections),当客户端连接数超过此上限时,新的连接请求将被拒绝。典型现象是在应用端看到数据库无法连接,并报出类似
FATAL: remaining connection slots are reserved for non-replication superuser connections 或 too many clients already 的错误。
这表示普通连接数已用完,仅剩下保留给超管或复制的槽位
诊断
连接耗尽通常由客户端大量并发请求引起。您可以通过 PGCAT Instance / PGCAT Database / PGCAT Locks 直接查阅数据库当前的活跃会话。 并判断是什么样的查询填满了系统,并进行进一步的处理。特别需要关注是否存在大量 Idle in Transaction 状态的连接以及长时间运行的事务(以及慢查询)。
处理
杀查询:对于已经耗尽导致业务受阻的情况,通常立即使用 pg_terminate_backend(pid) 进行紧急降压。
对于使用连接池的情况,则可以调整连接池大小参数,并执行 reload 重载的方式减少数据库层面的连接数量。
您也可以修改 max_connections 参数为更大的值,但本参数需要重启数据库后才能生效。
etcd 配额写满
etcd 配额写满将导致 PG 高可用控制面失效,无法进行配置变更。
诊断
Pigsty 在实现高可用时使用 etcd 作为分布式配置存储(DCS),etcd 自身有一个存储配额(默认约为2GB)。 当 etcd 存储用量达到配额上限时,etcd 将拒绝写入操作,报错 “etcdserver: mvcc: database space exceeded"。在这种情况下,Patroni 无法向 etcd 写入心跳或更新配置,从而导致集群管理功能失效。
解决
在 Pigsty v2.0.0 - v2.5.1 之间的版本默认受此问题影响。Pigsty v2.6.0 为部署的 etcd 新增了自动压实的配置项,如果您仅将其用于 PG 高可用租约,则常规用例下不会再有此问题。
有缺陷的存储引擎
目前,TimescaleDB 的试验性存储引擎 Hypercore 被证实存在缺陷,已经出现 VACUUM 无法回收出现 XID 回卷故障的案例。 请使用该功能的用户及时迁移至 PostgreSQL 原生表或者 TimescaleDB 默认引擎
详细介绍:《PG新存储引擎故障案例》
2 - 误删处理
误删数据
如果是小批量 DELETE 误操作,可以考虑使用 pg_surgery 或者 pg_dirtyread 扩展进行原地手术恢复。
如果被删除的数据已经被 VACUUM 回收,那么使用通用的误删处理流程。
误删对象
当出现 DROP/DELETE 类误操作,通常按照以下流程决定恢复方案。
- 确认此数据是否可以通过业务系统或其他数据系统找回,如果可以,直接从业务侧修复。
- 确认是否有延迟从库,如果有,推进延迟从库至误删时间点,查询出来恢复。
- 如果数据已经确认删除,确认备份信息,恢复范围是否覆盖误删时间点,如果覆盖,开始 PITR
- 确认是整集群原地 PITR 回滚,还是先 克隆新集群 验证数据,还是用从库来重放,并执行恢复策略
误删集群
如果出现整个数据库集群通过 Pigsty 管理命令被误删的情况,例如错误的执行 pgsql-rm.yml 剧本或 bin/pgsql-rm 命令。
除非您指定了 pg_rm_backup 参数为 false,否则备份会与数据库集群一起被删除。
警告:在这种情况,您的数据将无法找回!请务必三思而后行!
建议:对于生产环境,您可以在配置清单中全局配置此参数为 false,在移除集群时保留备份。
3 - 手工 PITR 演练
本教程在 Pigsty v4.5.0 的四节点沙箱中演练 PostgreSQL 时间点恢复。核心路径使用 pgsql-pitr.yml 的 down → pitr → up 三阶段,让操作者在覆盖数据、提升时间线和重建 HA 之前分别停下来验证。
如果只恢复当前节点,可使用 pig pitr;如果需要直接控制 pgBackRest,可参考 pg-pitr 低层工具。
恢复会停止 Patroni/PostgreSQL,并以 pgbackrest --force restore 覆盖目标 PGDATA;up 阶段还会删除目标集群的 etcd 前缀并重建 Patroni 状态。剧本会打印计划,但 没有交互确认。生产操作前必须由操作者明确说出并确认精确集群名与恢复点,核对近期可用且独立验证过的备份,使用完全相同的 -l、变量与标签先运行 --check,并安排维护窗口。本教程不授权在任何生产环境执行这些命令。
准备隔离沙箱
使用 Vagrant 或其他可丢弃的四节点实验环境,并选用自带 Silo 备份仓库的 ha/full 模板:
ha/full 定义单节点 pg-meta、三节点 pg-test 与 Silo/pgBackRest 仓库。本教程以下使用精确目标 pg-meta,避免把示例选择器复制到其他环境。
初始部署和备份都会改变沙箱状态;生产环境必须另行履行部署与备份审批流程。
建立恢复证据
先只读检查拓扑、备份链与 WAL 范围:
info 中至少应有 status: ok 的可用备份,并且归档 WAL 覆盖目标时间。check 能检查当前 stanza 与归档链路,但不能替代真实恢复演练或独立副本验证。
在沙箱中,可以运行 Pigsty 心跳脚本生成易验证的时间序列:
记录以下信息,随后停止负载:
- 准备恢复到的带时区时间戳;
- 该时刻前后的心跳、LSN 与事务边界;
- 当前主库、时间线和备份标签;
- 目标集群名
pg-meta与目标节点。
对真实业务表的检查需要单独授权;本教程只使用沙箱心跳数据。
声明恢复任务
在沙箱清单的 pg-meta.vars 中声明恢复目标:
cluster是备份源 stanza;缺省为目标pg_cluster。action: pause让 PostgreSQL 到达目标后暂停,给人工验证留下闸门。archive: true保留归档设置。backup: true不是安全备份替代品:它会先删除已有的<pg_data>-backup,再移动当前 PGDATA,因此这里保持false。
也可以用 -e 临时传入同一对象,但三个阶段与预检必须逐字复用同一份有效 JSON,避免变量漂移。
完整预检
在任何停服或写入动作前,对完整工作流执行同目标预检:
检查 Ansible 解析出的唯一目标确实是 pg-meta,并核对输出中的:
- 源 stanza、恢复类型、时间、时间线与动作;
- 目标
pg_data、端口和仓库; - 表空间/软链接映射;
archive与backup行为。
--check 只验证清单、变量和任务选择,不能证明 pgBackRest 备份能够恢复。目标、备份或变量一旦变化,就必须重新预检。
阶段一:停服
只有在操作者再次确认精确目标 pg-meta、恢复点与维护窗口后,才执行:
down 会尝试暂停 Patroni 自动故障转移,停止所有目标成员的 Patroni,并在 PostgreSQL 仍运行时执行 immediate shutdown。随后在每个目标节点确认服务确实停止;不要只相信剧本返回码:
预期分别为 inactive 和“server is not running”。如果任何成员仍在运行,停止流程并排障,不要进入恢复阶段。
阶段二:恢复并验证
再次核对 pg_pitr 与目标节点后执行破坏性的恢复阶段:
该阶段会:
- 生成
/pg/conf/pitr.conf与/pg/bin/pg-restore; - 根据
backup决定是否移动原 PGDATA; - 创建目标目录并运行带
--force、delta=y的 pgBackRest restore; - 直接启动 PostgreSQL并等待日志出现 consistent recovery state;
- 打印
pg_controldata摘要。
控制信息只能证明数据目录具有可读的控制状态,不能证明指定时间、XID 或业务状态正确。使用 action: pause 时,确认 WAL 已到达并暂停在目标附近:
然后只检查获授权的最小数据范围;在沙箱中可检查心跳记录。若目标不对:
- 保持所有 Patroni 停止;
- 停止手工启动的 PostgreSQL;
- 修改恢复目标并重新运行完整
--check; - 再执行
pitr阶段。
不要运行 up,也不要让旧时间线上的副本重新接入。
提升与阶段三:重建 HA
只有操作者确认恢复结果正确且接受创建新时间线后,才提升恢复实例:
预期结果为 f。提升不是只读验证,也不能无损撤销。
在所有 Patroni 成员仍停止、精确目标仍为 pg-meta 的前提下,再执行:
up 会在主库节点对应的 etcd 中删除 /pg/pg-meta/ 前缀(实际前缀还受 pg_namespace/Citus 配置影响),停止手工 PostgreSQL,启动主库 Patroni,再逐个启动副本并恢复 HA。etcd 删除任务设置了错误容忍,因此成功返回也不能证明旧 DCS 状态已正确清除。
恢复后验收
逐项验证,不要把“服务启动”当成恢复完成:
还应确认:
- 只有预期成员成为主库,副本来自新时间线且复制正常;
- HAProxy/VIP/DNS 和应用流量只指向已验收的实例;
- 恢复点附近的数据与事件边界正确;
archive_mode、archive_command和新 WAL 归档正常;- 监控、告警与备份仓库没有旧集群残留。
确认新时间线稳定后,按审批流程执行新的全量备份并再次核验:
如果本次恢复显式使用了 archive: false,它会写入 archive-mode=off。只有在验证恢复结果并确认维护窗口后,才重置该覆盖项并通过受控重启使 archive_mode 生效;默认 archive: true 不需要这一步。
多节点与跨集群恢复
- 多节点恢复后,旧时间线副本不能未经验证直接重新加入;
up会逐个启动副本并等待克隆/恢复,必须监控完成状态。 - 从另一 stanza 恢复时,
pg_pitr.cluster是源,-l仍是被覆盖的目标。把两者分别写进变更单并逐一复述。 - 跨集群恢复通常应使用
archive: false,避免测试目标向源 stanza 写入 WAL;验收并完成 stanza 善后 后再启用自己的归档。 link_map、data、port与临时repo会改变真正的数据与存储目标,必须纳入--check和人工复核。
相关文档
4 - 克隆与旁路恢复 PostgreSQL 实例
Pigsty v4.5.0 提供两个本机 Shell 工具:
它们适合沙箱演练、旁路取证和临时测试,不是完整的 Patroni 集群恢复编排器。托管实例优先使用 pig pitr;多节点集群优先使用分阶段的 pgsql-pitr.yml。
pg-fork 会递归删除已存在的目标目录;pg-pitr 会用备份覆盖目标目录。两者在非交互环境都可能不经确认直接执行。真实运行前必须核对源与目标的绝对路径、端口、表空间、精确集群/实例身份,并确认有独立、近期且经过验证的备份。不要把刚创建的 CoW 克隆当作独立备份。
pg-fork
pg-fork 在当前节点上复制 PostgreSQL 数据目录。以数据库操作系统用户(通常为 postgres,至少属于 postgres 组)执行:
参数
| 参数 | 含义 | 默认值 |
|---|---|---|
<FORK_ID> |
单个数字 1–9,用于推导默认目录和端口 |
必填 |
-d, --data <path> |
源数据目录 | $PG_DATA 或 /pg/data |
-D, --dst <path> |
目标数据目录 | /pg/data<FORK_ID> |
-p, --port <port> |
源实例端口 | $PG_PORT 或 5432 |
-P, --dst-port <port> |
目标实例端口 | <FORK_ID>5432 |
-s, --skip |
跳过在线备份 API,强制冷拷贝 | 否 |
-y, --yes |
跳过交互确认 | 否 |
脚本会拒绝相同的规范化源/目标路径,但不会判断自定义目标目录是否属于其他重要数据。目标目录存在时,它会在复制前执行递归删除。
热备份与冷拷贝
默认情况下,脚本用目标端口连接源实例,在同一个 psql 会话中执行:
CHECKPOINT;pg_backup_start();rm -rf <目标>与cp -a --reflink=auto;pg_backup_stop(wait_for_archive => false)。
如果无法通过指定端口连接源实例,脚本会 自动降级为冷拷贝,而不是中止。-s 也会强制冷拷贝。只有确认源实例已经完全停止时,冷拷贝才是安全的;postmaster.pid 只能作为警告线索,不能证明进程状态。
同一文件系统上,脚本会将以下文件系统识别为快速 CoW 模式:启用 reflink 的 XFS、Btrfs、Bcachefs 和 OCFS2。其他文件系统或跨文件系统目标仍执行 cp --reflink=auto,但可能退化为完整复制。脚本帮助中的 ZFS 描述比当前探测逻辑更宽;v4.5.0 实现不会把 ZFS 标记为已确认的快速 CoW 模式。
副本配置
复制成功后,pg-fork 会:
- 删除目标中的
postmaster.pid、postmaster.opts与standby.signal; - 清空目标中的物理复制槽目录;
- 在目标
postgresql.auto.conf中设置独立port、archive_mode=off与本地log_directory; - 删除
primary_conninfo、primary_slot_name与旧的recovery_target*覆盖项。
脚本不会检查目标端口是否空闲,也不会调整内存参数。启动副本前,至少核对:
cp -a 会保留 pg_tblspc 中的符号链接;pg-fork 不会复制或重映射 PGDATA 之外的表空间。直接启动这样的副本可能访问甚至修改源实例的表空间。存在外部表空间时,必须先独立复制并重映射所有表空间,或不要使用此脚本创建可写副本。
交互边界
只有标准输入是终端且没有 -y 时,脚本才询问 Proceed with fork? [y/N]。管道、CI、cron 等非交互调用不会出现该确认。因此自动化必须在调用前自行完成严格的绝对路径白名单与目标存在性检查;不要为了方便默认添加 -y。
pg-pitr
pg-pitr 是低层 pgBackRest restore 包装器。它不暂停或启动 Patroni,不停止或启动 PostgreSQL,不清理 DCS,也不重建副本。
恢复目标
实际执行至少要明确理解一个恢复目标。无参数调用只显示帮助:
| 参数 | pgBackRest 语义 |
|---|---|
-d, --default |
不设置停止目标,重放到可用 WAL 末尾 |
-i, --immediate |
到达所选备份的一致性点后停止 |
-t, --time <timestamp> |
恢复到指定时间 |
-n, --name <restore-point> |
恢复到命名还原点 |
-l, --lsn <lsn> |
恢复到指定 LSN |
-x, --xid <xid> |
恢复到指定事务 ID |
-S/--set(兼容别名 -b/--backup)只选择 从哪个备份集开始恢复,不是停止目标。例如,-S 20251225-120000F -d 仍会继续重放到 WAL 末尾;若要在该备份一致后立即停止,应组合 -S ... -i。
针对 time、name、lsn、xid 与 immediate,pgBackRest 的有效默认动作是抵达目标后暂停;-P/--promote 改为自动提升。-X/--exclusive 只应与 time、lsn 或 xid 这类明确边界配合使用。
其他选项
| 参数 | 含义 |
|---|---|
-D, --data <path> |
目标数据目录,必须是绝对路径;默认 /pg/data |
-s, --stanza <name> |
pgBackRest stanza;默认从配置取第一个非 global stanza |
-T, --timeline <value> |
latest、current 或正整数时间线 |
-P, --promote |
对有停止目标的恢复设置自动提升 |
-v, --verbose |
启用 pgBackRest info 级控制台日志 |
-c, --check, --dry-run |
只打印将执行的命令 |
-y, --yes |
跳过五秒倒计时 |
-- <args> |
将额外参数原样传给 pgBackRest |
-c 是命令渲染检查,不会证明备份/WAL 可用,也不会检查 PostgreSQL 或 Patroni 已停止。额外 pgBackRest 参数也没有由包装器做冲突过滤;传递仓库、表空间或链接映射参数时必须单独审查最终命令。
安全执行顺序
以下示例只展示单个已隔离目标目录的低层流程;生产集群恢复应使用完整 runbook:
实际执行拒绝 root,并在发现目标目录中存在 postmaster.pid 时中止;即使 PID 已失效,也要求人工确认后清理。它没有 y/N 问答:交互终端只有五秒可中断倒计时,非交互环境没有倒计时并直接进入 restore。
恢复后由操作者启动实例并验证:
只有恢复目标、允许访问的业务数据、时间线和归档设置全部验证无误后,才决定是否提升。提升会创建新时间线,不是可撤销的“查看”动作。pg-pitr 本身不会关闭归档;不要机械执行脚本结尾的通用“enable archive_mode”提示,应先查看有效值,只纠正本次恢复明确造成的覆盖项。
旁路恢复的额外风险
向 /pg/data1 之类的自定义目录恢复时,pgBackRest 可能从备份恢复 postgresql.auto.conf,覆盖 pg-fork 写入的独立端口。启动前重新检查 port、archive_mode、socket、日志与内存设置。
备份中若包含外部表空间或链接,旁路恢复还可能使用原路径。需要隔离时,应在 -- 后提供经过审查的 pgBackRest --tablespace-map、--link-map 等参数,并检查打印出的完整命令;否则不要在与生产实例相同的主机上启动恢复副本。
推荐的克隆验证流程
- 核对源实例、目标绝对路径、目标端口、表空间与独立备份。
- 在交互终端运行
pg-fork <id>,确认脚本显示的是热备份而非意外降级的冷拷贝。 - 不启动副本,先用
pg-pitr -D <clone> ... -c检查恢复命令。 - 明确确认目标后执行恢复;随后重新检查副本端口和所有外部路径。
- 启动副本,在隔离端口上验证恢复状态和经授权的数据。
- 只有需要形成新主库时才提升;否则停止副本并按经过验证的精确路径清理。
这种旁路验证可以降低对当前 PGDATA 的直接影响,但仍会读取同一个备份仓库、占用主机资源,并可能触及外部表空间;它不是无风险沙箱。
相关文档
5 - 为 PostgreSQL 集群启用 HugePage
使用
node_hugepage_count和node_hugepage_ratio或/pg/bin/pg-tune-hugepage
如果你计划启用大页(HugePage),请考虑使用 node_hugepage_count 和 node_hugepage_ratio,并配合 ./node.yml -t node_tune 进行应用。
大页对于数据库来说有利有弊,利是内存是专门管理的,不用担心被挪用,降低数据库 OOM 风险。缺点是某些场景下可能对性能由负面影响。
在 PostgreSQL 启动前,您需要分配 足够多的 大页,浪费的部分可以使用 pg-tune-hugepage 脚本对其进行回收,不过此脚本仅 PostgreSQL 15+ 可用。
如果你的 PostgreSQL 已经在运行,你可以使用下面的办法启动大页(仅 PG15+ 可用):
6 - 3坏2应急处理
如果经典3节点高可用部署同时出现两台(多数主体)故障,系统通常无法自动完成故障切换,需要人工介入:
首先判断另外两台服务器的情况,如果短时间内可以拉起,优先选择拉起另外两台服务。否则进入 紧急止血流程
紧急止血流程假设您的管理节点故障,只有单台普通数据库节点存活,在这种情况下,最快的恢复操作流程为:
- 调整 HAProxy 配置,将流量指向主库。
- 关闭 Patroni,手动提升 PostgreSQL 从库为主库。
调整HAProxy配置
如果你通过其他方式绕开 HAProxy 访问集群,那么可以跳过这一步。 如果你通过 HAProxy 方式访问数据库集群,那么你需要调整负载均衡配置,将读写流量手工指向主库。
- 编辑
/etc/haproxy/conf.d/<pg_cluster>-primary.cfg配置文件,其中<pg_cluster>为你的 PostgreSQL 集群名称,例如pg-meta。 - 将健康检查配置选项注释,停止进行健康检查。
- 将服务器列表中,其他两台故障的机器注释掉,只保留当前主库服务器。
配置调整完成后,先不着急执行 systemctl reload haproxy 重载生效,等待后续主库提升后一起执行。
以上配置的效果是,HAProxy 将不再进行主库健康检查(默认使用 Patroni),而是直接将写入流量指向当前主库
手工提升备库
登陆目标服务器,切换至 dbsu 用户,执行 CHECKPOINT 刷盘后,关闭 Patroni,重启 PostgreSQL 并执行 Promote。
如果你上面调整了 HAProxy 配置,那么现在可以执行 systemctl reload haproxy 重载 HAProxy 配置,将流量指向新的主库。
避免脑裂
紧急止血后,第二优先级问题为:避免脑裂。用户应当防止另外两台服务器重新上线后,与当前主库形成脑裂,导致数据不一致。
简单的做法是:
- 将另外两台服务器直接 断电/断网,确保它们不会在不受控的情况下再次上线。
- 调整应用使用的数据库连接串,将其 HOST 直接指向唯一幸存服务器上的主库。
然后应当根据具体情况,决定下一步的操作:
- A:这两台服务器是临时故障(比如断网断电),可以原地修复后继续服务
- B:这两台故障服务器是永久故障(比如硬件损坏),将移除并下线。
临时故障后的复原
如果另外两台服务器是临时故障,可以修复后继续服务,那么可以按照以下步骤进行修复与重建:
- 每次处理一台故障服务器,优先处理 管理节点 / INFRA 管理节点
- 启动故障服务器,并在启动后关停 Patroni
ETCD 集群在法定人数恢复后,将恢复工作,此时可以启动幸存服务器(当前主库)上的 Patroni,接管现有 PostgreSQL,并重新获取集群领导者身份。 Patroni 启动后进入维护模式。
在另外两台实例上以 postgres 用户身份创建 touch /pg/data/standby.signal 标记文件将其标记为从库,然后拉起 Patroni:
确认 Patroni 集群身份/角色正常后,退出维护模式:
永久故障后的复原
出现永久故障后,首先需要恢复管理节点上的 ~/pigsty 目录,主要是需要 pigsty.yml 与 files/pki/ca/ca.key 两个核心文件。
如果您无法取回或没有备份这两个文件,您可以选择部署一套新的 Pigsty,并通过 备份集群 的方式将现有集群迁移至新部署中。
请定期备份
pigsty目录(例如使用 Git 进行版本管理)。建议吸取教训,下次不要犯这样的错误。
配置修复
您可以将幸存的节点作为新的管理节点,将 ~/pigsty 目录拷贝到新的管理节点上,然后开始调整配置。
例如,将原本默认的管理节点 10.10.10.10 替换为幸存节点 10.10.10.12
ETCD修复
然后执行以下命令,将 ETCD 重置为单节点集群:
根据 ETCD重载配置 的说明,调整对 ETCD Endpoint 的引用。
INFRA修复
如果幸存节点上没有 INFRA 模块,请在当前节点上配置新的 INFRA 模块并安装。执行以下命令,将 INFRA 模块部署到幸存节点上:
修复当前节点的监控
PGSQL修复
各模块修复后,您可以参考标准扩容流程,将新的节点加入集群,恢复集群的高可用性。
7 - 使用 VIP-Manager 为 PostgreSQL 集群配置二层 VIP
您可以在 PostgreSQL 集群上绑定一个可选的 L2 VIP —— 前提条件是:集群中的所有节点都在一个二层网络中。
这个 L2 VIP 强制使用 Master - Backup 模式,Master 始终指向在数据库集群主库实例所在的节点。
这个 VIP 由 VIP-Manager 组件管理,它会从 DCS (etcd) 中直接读取由 Patroni 写入的 Leader Key,从而判断自己是否是 Master。
启用VIP
在 PostgreSQL 集群上定义 pg_vip_enabled 参数为 true,即可在集群上启用 VIP 组件。当然您也可以在全局配置中启用此配置项。
请注意,pg_vip_address 必须是一个合法的 IP 地址,带有网段,且在当前二层网络中可用。
pg_vip_interface 默认为 auto,
此时 Pigsty 会根据 inventory 中的 IPv4 地址自动探测各实例使用的网卡。
如果自动探测不适用于非标准路由或策略路由环境,可以为每个实例显式指定合法的网卡名称,例如:
使用以下命令,刷新 PG 的 vip-manager 配置并重启生效:
8 - Citus 集群部署
Citus 是一个 PostgreSQL 扩展,可以将 PostgreSQL 原地转换为一个分布式数据库,并实现在多个节点上水平扩展,以处理大量数据和大量查询。
Patroni 在 v3.0 后,提供了对 Citus 原生高可用的支持,简化了 Citus 集群的搭建,Pigsty 也对此提供了原生支持。
Citus 13.x 支持 PostgreSQL 18、17、16、15、14 五个大版本。Pigsty 扩展仓库提供了 Citus ARM64 软件包。
Citus集群
Pigsty 原生支持 Citus。当前完整配置模板见 conf/ha/citus.yml。
下面先使用一个简化的四节点拓扑说明关键参数:一个两节点协调者集群 pg-citus0,以及两个单节点 Worker 集群 pg-citus1、pg-citus2。它不是当前完整模板的逐行摘录。
相比标准 PostgreSQL 集群,Citus 集群的配置有一些特殊之处,首先,你需要确保 Citus 扩展被下载,安装,加载并启用,这涉及到以下四个参数
repo_packages:必须包含citus扩展,或者你需要使用带有 Citus 扩展的 PostgreSQL 离线安装包。pg_extensions:必须包含citus扩展,即你必须在每个节点上安装citus扩展。pg_libs:必须显式包含citus,并将其置于首位;当前 Patroni 模板直接使用该参数生成shared_preload_libraries。pg_databases: 这里要定义一个首要数据库,该数据库必须安装citus扩展。
其次,你需要确保 Citus 集群的配置正确:
pg_mode: 必须设置为citus,从而告知 Patroni 使用 Citus 模式。pg_primary_db:必须指定一个首要数据库的名称,该数据库必须安装citus扩展,这里名为citus。pg_shard:必须指定一个统一的名称,字符串,作为所有水平分片 PG 集群的集群名称前缀,这里为pg-citus。pg_group:必须指定一个分片号,从零开始依次分配的整数,0号固定代表协调者集群,其他为 Worker 集群。pg_cluster必须在每个物理 PostgreSQL 集群间唯一。通常采用pg_shard加序号的命名约定,但当前角色不强制它与pg_group按字符串拼接结果相等。pg_dbsu_password:必须设置为非空的纯文本密码,否则 Citus 无法正常工作。pg_parameters:建议设置citus.node_conninfo参数,强制要求 SSL 访问并要求节点间验证客户端证书。
配置完成后,您可以像创建普通 PostgreSQL 集群一样,使用 pgsql.yml 部署 Citus 集群。
管理Citus集群
定义好 Citus 集群后,部署 Citus 集群同样使用的剧本 pgsql.yml:
使用任意成员的 DBSU(postgres)用户,都能通过 patronictl (alias: pg) 列出 Citus 集群的状态:
您可以将每个水平分片集群视为一个独立的 PGSQL 集群,使用 pg (patronictl) 命令管理它们。
但是务必注意,当你使用 pg 命令管理 Citus 集群时,需要额外使用 --group 参数指定集群分片号
Citus 中有一个名为 pg_dist_node 的系统表,用于记录 Citus 集群的节点信息,Patroni 会自动维护该表。
此外,你还可以查看用户认证信息(仅限超级用户访问):
然后,你可以使用普通业务用户(例如,具有 DDL 权限的 dbuser_citus)来访问 Citus 集群:
使用Citus集群
在使用 Citus 集群时,我们强烈建议您先阅读 Citus 官方文档,了解其架构设计与核心概念。
其中核心是了解 Citus 中的五种表,以及其特点与应用场景:
- 分布式表(Distributed Table)
- 参考表(Reference Table)
- 本地表(Local Table)
- 本地管理表(Local Management Table)
- 架构表(Schema Table)
在协调者节点上,您可以创建分布式表和引用表,并从任何数据节点查询它们。从 11.2 开始,任何 Citus 数据库节点都可以扮演协调者的角色了。
我们可以使用 pgbench 来创建一些表,并将其中的主表(pgbench_accounts)分布到各个节点上,然后将其他小表作为引用表:
执行读写测试:
更严肃的生产部署
要将 Citus 用于生产环境,您通常需要为 Coordinator 和每个 Worker 集群设置流复制物理副本。
当前 conf/ha/citus.yml 在 13 台主机上定义了 1 个 pg-meta 实例,以及 12 个 Citus 实例(6 个双节点物理集群,pg_group 为 0–5)。下面的 10 节点片段是另一种独立的生产拓扑示例,并非当前模板内容。
我们将在后续教程中覆盖一系列关于 Citus 的高级主题
- 读写分离
- 故障处理
- 一致性备份与恢复
- 高级监控与问题诊断
- 连接池