☰
Ubuntu 上 PostgreSQL 主从复制自动化部署脚本实战
2026/10/3 3:36:36 网站建设 项目流程

1. 部署前的思路与方案选型

1.1 为什么选 Ubuntu + PostgreSQL 这套组合

先说结论:Ubuntu 是目前跑 PostgreSQL 主从复制最省心的 Linux 发行版之一,尤其是 22.04 LTS 和 24.04 LTS 这两个长期支持版本。

很多做运维的朋友喜欢用 CentOS 系,但说实话,在 PostgreSQL 生态里 Ubuntu 的优势非常明显:官方 apt 源里 PostgreSQL 的版本很全,从 14 到 17 都能直接装,不用像 CentOS 那样折腾 EPEL 或者第三方 RPM 仓库。而且 Ubuntu 的 systemd 单元文件写得比较规整,服务管理、开机自启、日志输出都一目了然,排查问题的时候少走很多弯路。

我见过不少生产环境事故,其实不是 PostgreSQL 本身的问题,而是操作系统层面的坑:防火墙规则没放行、SELinux 拦截了连接、系统时区和数据库时区不一致导致主从时间线判断出错。Ubuntu 默认不开 SELinux(用的是 AppArmor,而且 PostgreSQL 没被默认强制限制),这个坑天然就少了一大半。再加上 Ubuntu 的文档和社区资料极其丰富,搜一个报错信息基本都能找到现成的解决方案。

版本选择上,如果你是新部署,我建议直接上 PostgreSQL 16 或 17。16 在流复制性能上有明显优化,比如逻辑复制支持从备用服务器同步,WAL 归档并行化更好。17 则在 WAL 管理上又做了改进,vacuum 进程的内存使用更智能。当然,如果你们内部有严格的版本管控,14 和 15 也完全够用,脚本逻辑是通用的,不需要为版本差异额外改动。

1.2 一主一从架构与流复制原理

一主一从架构,说穿了就是两台 PostgreSQL 服务器:一台主库(Primary)负责读写业务流量,一台从库(Standby)实时同步主库的数据,平时可以分担读请求,主库挂了还能顶上去。

这里必须把流复制(Streaming Replication)的原理讲透,因为脚本里所有配置都是围着它转的。PostgreSQL 主库会把每一次数据变更写入 WAL(Write-Ahead Logging)日志,也就是预写式日志。可以把它理解成一本流水账,数据库在真正修改数据文件之前,先把“将要做什么”记录在这本账本上。

流复制的核心逻辑就是:主库把 WAL 日志实时推送给从库,从库收到日志后在本地回放(Replay),从而保持与主库的数据一致。这个过程中有几个关键角色:

  • WAL sender:主库上的发送进程,负责通过网络把 WAL 日志发给从库。
  • WAL receiver:从库上的接收进程,负责接收主库发来的日志。
  • WAL writer:两边都有,负责把日志写入 WAL 文件。

从库上还有一个很关键的概念叫restore_command。当从库发现本地 WAL 文件有缺失时,会通过这个命令去拉取归档日志。如果没有配置归档,那从库就只能依赖流复制实时传来的日志。所以生产环境建议把归档也配上,否则从库宕机时间长了,主库的 WAL 文件被 recycling 覆盖,从库就断档了。我后面给的脚本里会同时把归档参数配置进去,这个很重要。

1.3 脚本化部署的价值边界

很多新手会觉得,主从复制嘛,手敲命令也能搞定,为什么要写脚本?我一开始也是这么想的,直到有一次在一台新服务器上手动部署,连续敲了两个小时命令,中途因为一个pg_hba.conf里的空格问题排查了半小时,最后还得靠pg_basebackup重新拉数据。那种感觉太痛苦了。

脚本化部署的核心价值,不是省那几十分钟的敲命令时间,而是消除人为失误的可能性。PostgreSQL 主从复制的配置项有十几个,每个参数的取值、位置、注释格式都有讲究,手敲很容易出错。而且一旦配置错了,排查起来极其耗时——不同的错误往往表现为同样的症状:从库起不来,或者起来了但状态是in recovery。

我写这个脚本的定位是:一次执行、全套配置、输出可验证。你不需要懂得每个参数背后的原理,只需要回答几个简单的变量(主库 IP、从库 IP、复制账号密码),脚本会自动完成从安装、配置、防火墙放行、主从拉取到状态验证的全部流程。当然,脚本只是工具,原理还是要懂的,所以我下面会把脚本里每一段配置的“为什么”讲清楚。

2. 核心细节解析与关键参数

2.1 postgresql.conf 里的“命门”参数

主从复制的灵魂全在postgresql.conf里。很多部署失败的案例,查到最后都是这几个参数没配对。我把最关键的几个列出来,逐个说清楚。

首先是wal_level = replica。默认值是replica,但有些安装包默认是minimal,如果你从低版本升级上来尤其要注意。minimal级别下 WAL 日志不包含足够的信息用于复制,等于直接废了流复制功能。这里顺带说一句,如果以后要上逻辑复制或 CDC 工具(比如 Debezium),需要设为logical,物理复制用replica就够了。

然后是max_wal_senders。这个参数定义了主库最多能同时给多少个从库发送 WAL 日志。一主一从设置为 5 比较合适,留出余量给未来可能的扩展。注意这个参数不能小于从库数量加 1,因为主库还需要一个 sender 给自己做 pg_basebackup。

接下来是wal_keep_size(旧版本叫wal_keep_segments)。这个参数决定了主库保留多少 WAL 日志不清理,用于给掉线的从库补数据。我建议设置为 1GB(即 1024),这样即使从库断连一段时间,主库的 WAL 还在,从库重新连接后能自动补齐。如果没设这个值,从库掉线超过一定时间,主库的 WAL 被 recycled 覆盖,从库就彻底追不上了,只能手动重新做 basebackup。

还有hot_standby = on。这个参数配在从库上,允许从库在恢复模式下接受只读查询。如果你有读写分离的需求,这个必须开。

最后是archive_mode = on和archive_command。虽然流复制不强制要求归档,但生产环境强烈建议配上。我习惯用cp %p /var/lib/postgresql/wal_archive/%f这种方式,把 WAL 归档到本地目录,配合restore_command做兜底。如果暂时不想配归档,至少在脚本里留出注释和接口,以后需要的时候打开就行。

这些参数配完后,必须重启 PostgreSQL 服务才能生效。而且要注意:从库的postgresql.conf里,wal_level、max_wal_senders这些参数其实可以保持和主库一致,省得以后主从切换时还要改配置。

2.2 pg_hba.conf 与复制账号的权限模型

主从复制的认证环节,90% 的部署失败都栽在pg_hba.conf配置上。这个文件是 PostgreSQL 的“门禁系统”,定义了哪些 IP、哪些用户、用什么方式认证可以访问数据库。

复制账号必须单独创建,不要用超级用户postgres来复制,这是基本的安全底线。复制账号只需要REPLICATION权限就够了,执行下面的 SQL 创建:

CREATE ROLE replica WITH REPLICATION LOGIN PASSWORD 'you_strong_password';

然后,在pg_hba.conf里添加一行,允许从库 IP 以md5或scram-sha-256方式认证连接复制服务:

# 允许从库 IP 进行流复制连接 host replication replica 192.168.1.20/32 scram-sha-256

这里有两个细节容易踩坑:

第一,认证方式。PostgreSQL 16 开始默认用scram-sha-256,如果你还写成md5,密码认证会失效。所以要么统一用scram-sha-256,要么在postgresql.conf里设置password_encryption = scram-sha-256。

第二,IP 掩码。如果你写的是192.168.1.0/24表示整个网段都能连,这在测试环境无所谓,生产环境建议精确到具体 IP。我遇到过有人写错掩码导致其他机器也能连复制服务,这等于把数据库裸奔了。

还有一个很容易被忽略的点:pg_hba.conf是从上往下匹配的,第一条匹配的规则生效。如果你的文件里已经有一行host all all 0.0.0.0/0 reject,那必须把复制认证的规则放在它前面,否则永远匹配到拒绝规则,从库永远连不上。

2.3 从库创建方式:pg_basebackup 到底在干什么

主库配置完成后,创建从库的标准方式是pg_basebackup。这个工具的作用是:以文件系统级别拷贝主库的数据目录,并启动 WAL 日志的持续传输。

具体来说,它做了三件事:

  1. 连接到主库,以副本身份请求一个基础备份。
  2. 将主库数据目录(包括表、索引、配置文件等)完整拷贝到从库指定目录。
  3. 自动生成一个backup_label文件,标记备份开始时主库的 WAL 位置。这样从库启动恢复时,能从这个位置继续拉取后续日志。

pg_basebackup有一个参数-R,会在从库自动生成standby.signal文件和postgresql.auto.conf文件,里面自动写入primary_conninfo连接信息。这是从库进入恢复模式的关键。

有个这里必须强调的坑:pg_basebackup 的目标目录必须为空,而且之前 PostgreSQL 的 data 目录如果存在,必须清空。很多新手在已有数据的服务器上跑 basebackup,结果报错directory exists and is not empty,就是这个原因。

执行示例:

sudo -u postgres pg_basebackup -h 192.168.1.10 -p 5432 -U replica \ -D /var/lib/postgresql/16/main \ -Fp -Xs -R -P

参数解释:

  • -h:主库 IP。
  • -p:主库端口。
  • -U:复制账号。
  • -D:从库数据目录。
  • -Fp:输出格式为普通目录。
  • -Xs:流式传输 WAL 日志,不等待归档。
  • -R:自动写入恢复配置。
  • -P:显示进度条。

执行完成后,从库的数据目录就已经就绪了。启动服务前,务必确认目录权限属于postgres用户,否则服务起不来。

3. 实战部署脚本与完整流程

3.1 环境准备与版本选择

开始之前,先把两台 Ubuntu 服务器的初始状态说清楚。我默认你的两台服务器都装了 Ubuntu 22.04 LTS 或 24.04 LTS,root 或 sudo 权限可用,能连通互联网(或者有内网 apt 镜像源),PostgreSQL 尚未安装。如果已经有 PostgreSQL 了,脚本会检测到并跳过安装步骤,这点不用担心。

版本选择上,我强烈建议直接装 PostgreSQL 16。为什么不是 14 或 15?因为 16 是当前关注度和稳定性都非常均衡的版本,社区插件、监控工具、第三方方案都跟得比较紧。当然 17 已经发布,如果你追求新特性可以上 17,但建议至少等几个补丁版本后进生产。

脚本会用 PostgreSQL 官方 apt 源来安装,而不是 Ubuntu 自带的老版本。Ubuntu 仓库里的 PostgreSQL 版本更新太滞后,比如 24.04 自带的还是 16 但很快会被 17 替代,你总不希望生产环境装个“半个版本落后”的数据库吧。官方源配置方式如下:

sudo install -d /usr/share/postgresql-common/pgdg sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list sudo apt update sudo apt install -y postgresql-16 postgresql-client-16

这里有个细节:apt.postgresql.org.asc这个密钥文件每个系统装一次就行,不要每台机器都重新下载导致密钥不一致。

装完之后,PostgreSQL 会自动创建一个postgres系统用户,并初始化一个默认的 data 目录。你不需要手动执行initdb,安装包已经全部搞定了。

3.2 主库部署脚本实现

下面的脚本我按“主库”和“从库”分开写,两个脚本都能独立执行。先看主库脚本:

#!/bin/bash # ============================================================ # PostgreSQL 16 主库自动化部署脚本 (Ubuntu) # 用法: sudo bash deploy_pg_master.sh # ============================================================ set -euo pipefail # 定义变量 PG_VERSION="16" MASTER_IP="192.168.1.10" # 主库 IP STANDBY_IP="192.168.1.20" # 从库 IP REPL_USER="replica" REPL_PASSWORD="ChangeMe_Strong" PG_DATA="/var/lib/postgresql/${PG_VERSION}/main" PG_CONF="${PG_DATA}/postgresql.conf" PG_HBA="${PG_DATA}/pg_hba.conf" WAL_ARCHIVE_DIR="/var/lib/postgresql/wal_archive" # 1. 安装 PostgreSQL if ! command -v psql &> /dev/null; then echo "==> 正在安装 PostgreSQL ${PG_VERSION} ..." sudo install -d /usr/share/postgresql-common/pgdg sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \ https://www.postgresql.org/media/keys/ACCC4CF8.asc echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \ | sudo tee /etc/apt/sources.list.d/pgdg.list sudo apt update sudo apt install -y postgresql-${PG_VERSION} else echo "==> 检测到 PostgreSQL 已安装,跳过安装步骤" fi # 2. 配置 WAL 归档目录 sudo mkdir -p "${WAL_ARCHIVE_DIR}" sudo chown -R postgres:postgres "${WAL_ARCHIVE_DIR}" # 3. 修改 postgresql.conf sudo tee -a "${PG_CONF}" > /dev/null <<EOF # -------------------- 主从复制配置 (自动生成) -------------------- listen_addresses = '${MASTER_IP}' wal_level = replica max_wal_senders = 5 wal_keep_size = 1024 hot_standby = on archive_mode = on archive_command = 'cp %p ${WAL_ARCHIVE_DIR}/%f' EOF # 4. 修改 pg_hba.conf,注意追加在文件末尾前(需保证在拒绝规则之前) sudo sed -i "/^host.*all.*0.0.0.0\/0.*reject/d" "${PG_HBA}" 2>/dev/null || true sudo tee -a "${PG_HBA}" > /dev/null <<EOF # -------------------- 主从复制认证 (自动生成) -------------------- host replication ${REPL_USER} ${STANDBY_IP}/32 scram-sha-256 EOF # 5. 创建复制账号 sudo -u postgres psql <<EOF DO \$\$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${REPL_USER}') THEN CREATE ROLE ${REPL_USER} WITH REPLICATION LOGIN PASSWORD '${REPL_PASSWORD}'; END IF; END \$\$; EOF # 6. 重启服务 sudo systemctl restart postgresql@${PG_VERSION}-main # 7. 验证 sudo -u postgres psql -c "SELECT version();" sudo -u postgres psql -c "SELECT pg_is_in_recovery();" echo "==> 主库部署完成,请按任意键继续配置从库..."

这个脚本有几点我要重点说明:

set -euo pipefail一上来就让脚本遇到任何错误就退出,避免“半成品”状态。如果某个命令失败了,脚本立刻停住,你可以在错误信息里直接定位到问题。

pg_hba.conf我用了sed -i删除已有的拒绝规则,然后再追加。这个操作有点激进,但考虑到脚本是面向新部署的,能避免“拒绝规则在前面导致匹配不到”的经典坑。如果你是在已有配置上叠加部署,请手动编辑文件而不是跑 sed。

复制账号的创建我用了DO $$匿名块,判断账号是否已存在,已存在就不重复创建,这样脚本可以安全地重复执行。

listen_addresses我直接写死了主库 IP,这比写*更安全。如果你希望主库同时监听多个网卡地址,可以写成'ip1, ip2'的形式。

3.3 从库部署脚本实现

从库脚本的核心动作是:安装 PostgreSQL,停掉服务,清空数据目录,用pg_basebackup从主库拉取数据,启动服务。完整实现如下:

#!/bin/bash # ============================================================ # PostgreSQL 16 从库自动化部署脚本 (Ubuntu) # 用法: sudo bash deploy_pg_standby.sh # 前提: 主库已执行 deploy_pg_master.sh,能通过 IP 访问 # ============================================================ set -euo pipefail PG_VERSION="16" MASTER_IP="192.168.1.10" STANDBY_IP="192.168.1.20" REPL_USER="replica" REPL_PASSWORD="ChangeMe_Strong" PG_DATA="/var/lib/postgresql/${PG_VERSION}/main" PG_CONF="${PG_DATA}/postgresql.conf" WAL_ARCHIVE_DIR="/var/lib/postgresql/wal_archive" # 1. 安装 PostgreSQL if ! command -v psql &> /dev/null; then echo "==> 正在安装 PostgreSQL ${PG_VERSION} ..." sudo install -d /usr/share/postgresql-common/pgdg sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \ https://www.postgresql.org/media/keys/ACCC4CF8.asc echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \ | sudo tee /etc/apt/sources.list.d/pgdg.list sudo apt update sudo apt install -y postgresql-${PG_VERSION} else echo "==> 检测到 PostgreSQL 已安装,跳过安装步骤" fi # 2. 停止服务,准备拉取数据 sudo systemctl stop postgresql@${PG_VERSION}-main || true # 3. 清空数据目录 (注意备份原有数据) echo "==> 清空从库数据目录: ${PG_DATA}" sudo rm -rf "${PG_DATA}" sudo mkdir -p "${PG_DATA}" sudo chown -R postgres:postgres "$(dirname "${PG_DATA}")" sudo -u postgres mkdir -p "${WAL_ARCHIVE_DIR}" sudo -u postgres chown postgres:postgres "${WAL_ARCHIVE_DIR}" # 4. 创建密码文件 (供 pg_basebackup 非交互认证) sudo tee /tmp/.pgpass_replica > /dev/null <<EOF ${MASTER_IP}:5432:replication:${REPL_USER}:${REPL_PASSWORD} EOF sudo chmod 600 /tmp/.pgpass_replica sudo chown postgres:postgres /tmp/.pgpass_replica # 5. 执行 pg_basebackup 拉取主库全量数据 echo "==> 开始从主库 ${MASTER_IP} 拉取基础备份..." sudo -u postgres PGPASSFILE=/tmp/.pgpass_replica pg_basebackup \ -h "${MASTER_IP}" \ -p 5432 \ -U "${REPL_USER}" \ -D "${PG_DATA}" \ -Fp -Xs -R -P # 6. 清理密码文件 sudo rm -f /tmp/.pgpass_replica # 7. 追加从库的相关配置 sudo tee -a "${PG_CONF}" > /dev/null <<EOF # -------------------- 从库复制配置 (自动生成) -------------------- hot_standby = on wal_level = replica max_wal_senders = 5 restore_command = 'cp ${WAL_ARCHIVE_DIR}/%f %p' primary_conninfo = 'host=${MASTER_IP} port=5432 user=${REPL_USER} password=${REPL_PASSWORD} application_name=standby1' EOF # 8. 追加恢复标记 (确保从库进入 standby 模式) sudo -u postgres touch "${PG_DATA}/standby.signal" # 9. 启动服务 sudo systemctl start postgresql@${PG_VERSION}-main # 10. 验证 sleep 2 sudo -u postgres psql -c "SELECT pg_is_in_recovery();" sudo -u postgres psql -c "SELECT status, sender_host FROM pg_stat_wal_receiver;" echo "==> 从库部署完成"

从库脚本里最值得关注的是第 4 步:我生成了一个.pgpass文件来传递密码。为什么不直接用PGPASSWORD=${REPL_PASSWORD}环境变量?因为pg_basebackup在某些情况下不会自动读取这个环境变量(取决于编译选项和版本),而.pgpass文件是官方推荐的、最稳妥的方式。

注意primary_conninfo里的application_name=standby1,这个参数很实用。在主库上执行pg_stat_replication查询时,你会看到这个从库的显示名称,多从库场景下能一眼区分各节点。

还有一个细节:standby.signal文件在 PostgreSQL 12 以后的版本里替代了旧的recovery.conf。只要这个文件存在,PostgreSQL 启动时就会自动进入恢复模式,作为从库运行。所以别小看这一步,忘了创建这个文件是从库起不来的头号原因之一。

3.4 联动验证:主从状态与数据同步检查

脚本跑完之后,别急着收工,主从是否真正同步了必须验证。我总结了一套“三层检查法”,简单有效:

第一层:检查主库视角的复制状态。在主库上执行:

SELECT client_addr, state, sync_state, replay_lag FROM pg_stat_replication;

这条 SQL 会列出所有连接上来的从库。state列显示streaming表示正在流式传输;sync_state显示async表示异步复制(默认);replay_lag表示从库回放延迟,正常情况应为 0 或极小值。

第二层:检查从库视角的恢复状态。在从库上执行:

SELECT pg_is_in_recovery();

返回true说明从库正在恢复模式,也就是正常工作。

第三层:实际数据同步测试。在主库建一个测试表,插入几行数据,然后在从库上查询确认能看到:

-- 主库执行 CREATE TABLE IF NOT EXISTS sync_test (id serial primary key, ts timestamptz default now()); INSERT INTO sync_test (ts) VALUES (now()) RETURNING *; -- 从库执行 SELECT count(*) FROM sync_test;

只要从库能查到新增的行,说明流复制链路已经通了。如果查不到,先别慌,看一下是不是有延迟,执行:

SELECT now() - pg_last_xact_replay_timestamp() AS replay_delay;

这个差值就是从库落后主库的时间。正常情况应该是零点几秒以内,如果这个值持续增长,就要检查网络带宽和主库的 WAL 生成速率了。

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

4.1 流复制起不来的六大高频原因

我在实际部署过程中,踩过和帮别人处理过太多流复制的坑,整理一份高频问题排查表:

症状可能原因排查命令 / 方法
从库启动失败,日志提示无法连接到主库主库防火墙未放行 5432 端口sudo ufw status或nc -vz 主库IP 5432
pg_basebackup 报密码错误pg_hba.conf 认证方式不匹配检查pg_hba.conf中 replication 行是md5还是scram-sha-256
从库无法启动,日志显示 data directory 权限不对数据目录属主不是 postgreschown -R postgres:postgres data目录
从库启动后一直处于 catchup 状态主库 WAL 生成过快,从库追不上监控pg_stat_replication的 replay_lag,调大wal_keep_size或配置归档
主从切换后,新主库无法接受写入忘记移除standby.signal文件rm -f ${PG_DATA}/standby.signal后重启
从库连接主库时提示replication slot不存在未配置 primary_slot_name 且主库要求使用 slot在从库postgresql.conf中配置primary_slot_name

这里特别说一下replication slot的问题。从 PostgreSQL 10 开始,官方推荐用物理复制槽(physical replication slot)来防止主库 WAL 被提前清理。但复制槽有副作用:如果从库长期离线,主库的 WAL 会一直保留,直到磁盘塞满。所以如果你不希望承担这个风险,可以用wal_keep_size作为替代方案,这也是我脚本里默认的做法。如果你要配置复制槽,需要在主库执行:

SELECT * FROM pg_create_physical_replication_slot('standby1_slot');

然后在从库的primary_conninfo里加上slot_name=standby1_slot。

4.2 主从延迟与切换后数据一致性

主从延迟是运行中最常见的性能问题。延迟的来源主要有三个:

网络带宽:如果主库的写并发很高,WAL 日志产生的速率会非常大。假设每秒产生 50MB 的 WAL,而你的内网带宽只有 100Mbps(约 12.5MB/s),那延迟必然越来越大。这种时候需要检查主库的pg_current_wal_lsn()和从库的pg_last_wal_replay_lsn(),换算成字节差来估算延迟量。

从库磁盘 IO:从库回放 WAL 本质上是持续的随机写操作,如果磁盘是机械硬盘,或者 IOPS 指标太低,回放速度跟不上主库的写入速度。建议从库至少用 SSD。

主库的synchronous_commit设置:如果你追求数据零丢失,可以打开同步复制(synchronous_commit = on且设置synchronous_standby_names),但这会降低主库的写入性能,因为每次提交都要等从库确认。我的建议是一主一从的生产环境先跑异步复制,监控延迟,确认稳定后再考虑同步。

关于切换,虽然这次脚本没有包含自动故障转移(那需要 Patroni 或 Repmgr 这类工具),但你必须知道手动切换的标准动作:

# 1. 在从库上停止恢复模式,提升为主库 sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main # 2. 验证新主库可写 sudo -u postgres psql -c "SELECT pg_is_in_recovery();" # 返回 false # 3. 把原主库降级为从库(如果有需要) # 在原主库上执行 pg_basebackup 重新拉取数据,或者用 pg_rewind 快速回退

pg_rewind是快速把旧主库变成新从库的工具,比重新pg_basebackup快得多。但注意:pg_rewind 要求旧主库开启了wal_log_hints = on或data_checksums = on,我上面的脚本没有加这个参数,如果你有切换需求,建议手动加上。

4.3 安全加固与日常巡检建议

主从复制链路涉及数据库的完整数据流,安全上不能光靠内网部署就松懈。分享几个我实践过并且认为很有必要的加固措施:

复制账号的权限最小化。前面已经说了,只给REPLICATION权限,不要给SUPERUSER。而且要定期轮换密码,轮换后别忘了更新从库的primary_conninfo里的密码和.pgpass文件。

启用 SSL 加密复制流量。在内网环境很多人觉得没必要,但我的建议是能开就开。在postgresql.conf里设置:

ssl = on ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'

然后在pg_hba.conf的复制行改成hostssl:

hostssl replication replica 192.168.1.20/32 scram-sha-256

这样即使有人在内网抓包,也拿不到密码和数据内容。

日常巡检方面,我建议每天定时执行以下检查,结果写入日志或监控系统:

-- 主库检查复制是否正常 SELECT client_addr, state, replay_lag > interval '5 minutes' AS lag_alert FROM pg_stat_replication; -- 从库检查恢复状态和落后时间 SELECT now() - pg_last_xact_replay_timestamp() AS delay;

再配合系统层面的检查:磁盘空间(df -h)、WAL 归档目录是否持续增长、PostgreSQL 的server.log里有没有segmentation fault或invalid memory alloc request size之类的异常。

我个人在实际操作中还有一个习惯:每次部署完主从复制,我都会在从库上执行一次SELECT pg_last_wal_replay_lsn(),把结果和主库的pg_current_wal_lsn()存到本地笔记里,作为基线。下次巡检时对比,就能很直观地看到延迟是否在增大。这个习惯看起来不起眼,但真到了排查问题的时候,能帮你省下不少时间。

最后再分享一个小技巧:脚本里我用的set -euo pipefail,但pg_basebackup在某些网络异常情况下可能会返回非零退出码,导致脚本中途退出。如果你在自动化编排工具(比如 Ansible、Jenkins)里执行这个脚本,建议在pg_basebackup命令后加一行检查:

if [ $? -ne 0 ]; then echo "pg_basebackup 失败,请检查主库状态和网络连接" exit 1 fi

这样失败的时机和原因一目了然,不会把错误带进后续的启动步骤。这套脚本我前后迭代了很多次,现在基本属于“拿过去就能用”的状态,希望这份实战记录能帮你少走几步弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询