📋 本系列第 4 篇 | 上一篇:PostgreSQL 备份恢复:pg_dump、pg_basebackup、增量备份实战

📋 文章信息
• 适用版本:PostgreSQL 15.4 / repmgr 5.4.1
• 部署架构:1 主 2 备(3 节点)
• 部署方式:源码编译安装(生产环境)
• 操作系统:CentOS 7.9 / Rocky Linux 8+
• 统一安装目录:/home/postgres/opt/

🖥️ 第一步:环境规划

1.1 节点规划

角色 主机名 IP 地址 端口 说明
主节点 pg-node1 192.168.118.10 5432 初始主库
备节点 1 pg-node2 192.168.118.11 5432 热备库
备节点 2 pg-node3 192.168.118.12 5432 热备库

1.2 目录结构(所有节点统一)

/home/postgres/opt/
├── pgsql/              ← PostgreSQL 安装目录
│   ├── bin/
│   ├── lib/
│   └── include/
├── data/               ← PostgreSQL 数据目录
│   ├── base/
│   ├── pg_wal/
│   └── postgresql.conf
├── repmgr/             ← repmgr 安装目录
│   ├── bin/
│   └── lib/
├── log/                ← 统一日志目录
│   ├── postgresql/
│   └── repmgr/
└── wal_archive/        ← WAL 归档目录

1.3 软件版本

软件 版本 下载地址
PostgreSQL 15.4 官方下载
repmgr 5.4.1 官方下载
操作系统 CentOS 7.9 最小化安装

1.4 系统准备(所有节点)

# 1. 创建 postgres 用户和组
sudo groupadd -g 1001 postgres
sudo useradd -u 1001 -g postgres -s /bin/bash -m postgres

# 2. 安装编译依赖
sudo yum install -y gcc gcc-c++ make readline-devel zlib-devel openssl-devel \
    libxml2-devel libxslt-devel wget git vim net-tools

# 3. 创建统一目录
sudo mkdir -p /home/postgres/opt/{pgsql,data,repmgr,log/wal_archive}
sudo chown -R postgres:postgres /home/postgres/opt
sudo chmod -R 700 /home/postgres/opt

# 4. 设置环境变量
sudo su - postgres
cat >> ~/.bash_profile << 'EOF'

# PostgreSQL 环境变量
export PGHOME=/home/postgres/opt/pgsql
export PGDATA=/home/postgres/opt/data
export PATH=$PGHOME/bin:$PATH
export LD_LIBRARY_PATH=$PGHOME/lib:$LD_LIBRARY_PATH

# repmgr 环境变量
export REPMGRHOME=/home/postgres/opt/repmgr
export PATH=$REPMGRHOME/bin:$PATH

# 日志目录
export PGLOG=/home/postgres/opt/log/postgresql
EOF

source ~/.bash_profile

✅ 验证环境变量:

echo $PGHOME
# /home/postgres/opt/pgsql

echo $PGDATA
# /home/postgres/opt/data

which postgres
# /home/postgres/opt/pgsql/bin/postgres

📦 第二步:编译安装 PostgreSQL 15.4

2.1 下载源码(所有节点)

cd /tmp
wget https://ftp.postgresql.org/pub/source/v15.4/postgresql-15.4.tar.gz
tar -xzf postgresql-15.4.tar.gz
cd postgresql-15.4

✅ 预期输出:

ls -lh postgresql-15.4.tar.gz
-rw-r--r-- 1 root root 31M Apr  2 10:00 postgresql-15.4.tar.gz

2.2 编译安装

# 配置编译选项(生产环境)
./configure --prefix=/home/postgres/opt/pgsql \
    --with-openssl \
    --with-libxml \
    --with-libxslt \
    --enable-thread-safety \
    --with-pgport=5432

# 编译(使用所有 CPU 核心)
make -j$(nproc)

# 安装
sudo make install

✅ 预期输出:

configure: creating ./config.status
config.status: creating GNUmakefile
...
make[1]: Entering directory `/tmp/postgresql-15.4/src/backend'
...
/usr/bin/mkdir -p '/home/postgres/opt/pgsql/bin'
/usr/bin/install -c  postgres '/home/postgres/opt/pgsql/bin'

2.3 初始化数据库

# 切换到 postgres 用户
sudo su - postgres

# 初始化数据库(指定 UTF-8 编码)
initdb -D /home/postgres/opt/data -E UTF8 --locale=zh_CN.UTF-8

# 创建日志目录
mkdir -p /home/postgres/opt/log/postgresql

✅ 预期输出:

The files belonging to this database system will be owned by user "postgres".
...
Success. You can now start the database server using:

    /home/postgres/opt/pgsql/bin/pg_ctl -D /home/postgres/opt/data -l logfile start

🔐 第三步:配置 PostgreSQL

3.1 修改 postgresql.conf(所有节点)

# ==================== 连接设置 ====================
listen_addresses = '*'
port = 5432
max_connections = 200
superuser_reserved_connections = 3

# ==================== 复制配置 ====================
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
hot_standby = on
hot_standby_feedback = on
wal_keep_size = 2GB
synchronous_commit = on

# ==================== 归档配置 ====================
archive_mode = on
archive_command = 'cp %p /home/postgres/opt/wal_archive/%f'
archive_timeout = 300

# ==================== 日志配置 ====================
logging_collector = on
log_directory = '/home/postgres/opt/log/postgresql'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = 1d
log_rotation_size = 100MB
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_temp_files = 0
log_min_duration_statement = 1000

# ==================== 内存配置(根据实际内存调整) ====================
shared_buffers = 2GB
effective_cache_size = 6GB
work_mem = 64MB
maintenance_work_mem = 512MB

3.2 修改 pg_hba.conf(所有节点)

# ==================== 本地连接 ====================
local   all             all                                     scram-sha-256
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256

# ==================== 局域网连接 ====================
host    all             all             192.168.118.0/24        scram-sha-256

# ==================== repmgr 复制权限(使用 scram-sha-256 加密) ====================
local   replication     repmgr                                  scram-sha-256
host    replication     repmgr    192.168.118.10/32             scram-sha-256
host    replication     repmgr    192.168.118.11/32             scram-sha-256
host    replication     repmgr    192.168.118.12/32             scram-sha-256

⚠️ 重要:生产环境必须使用 scram-sha-256 加密,不要使用 trust!

3.3 启动 PostgreSQL

# 启动数据库
pg_ctl -D /home/postgres/opt/data -l /home/postgres/opt/log/postgresql/postgresql.log start

# 设置开机启动(systemd)
sudo vim /etc/systemd/system/postgresql-15.service
[Unit]
Description=PostgreSQL 15.4 Database Server
After=network.target

[Service]
Type=forking
User=postgres
Group=postgres
Environment="PGHOME=/home/postgres/opt/pgsql"
Environment="PGDATA=/home/postgres/opt/data"
Environment="PATH=/home/postgres/opt/pgsql/bin:/usr/bin:/sbin:/bin"
ExecStart=/home/postgres/opt/pgsql/bin/pg_ctl -D $PGDATA -l /home/postgres/opt/log/postgresql/postgresql.log start
ExecStop=/home/postgres/opt/pgsql/bin/pg_ctl -D $PGDATA stop
ExecReload=/home/postgres/opt/pgsql/bin/pg_ctl -D $PGDATA reload
PIDFile=/home/postgres/opt/data/postmaster.pid
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable postgresql-15
sudo systemctl start postgresql-15

# 验证服务状态
systemctl status postgresql-15

✅ 预期输出:

● postgresql-15.service - PostgreSQL 15.4 Database Server
   Loaded: loaded (/etc/systemd/system/postgresql-15.service; enabled)
   Active: active (running) since Wed 2026-04-02 17:00:00 CST

🔗 第四步:配置 SSH 免密登录

4.1 在主节点生成 SSH 密钥

sudo su - postgres
ssh-keygen -t ed25519 -b 512 -C "repmgr_cluster"

✅ 预期输出:

Generating public/private ed25519 key pair.
Enter file in which to save the key (/var/lib/pgsql/.ssh/id_ed25519): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /var/lib/pgsql/.ssh/id_ed25519.
Your public key has been saved in /var/lib/pgsql/.ssh/id_ed25519.pub.

4.2 复制公钥到所有节点

# 复制到自己的 authorized_keys
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

# 复制到其他节点(输入 postgres 用户密码)
ssh-copy-id -i ~/.ssh/id_ed25519.pub postgres@192.168.118.10
ssh-copy-id -i ~/.ssh/id_ed25519.pub postgres@192.168.118.11
ssh-copy-id -i ~/.ssh/id_ed25519.pub postgres@192.168.118.12

✅ 预期输出:

Number of key(s) added: 1

4.3 验证 SSH 免密

ssh postgres@192.168.118.11 "hostname && whoami"
ssh postgres@192.168.118.12 "hostname && whoami"

✅ 预期输出:

pg-node2
postgres
pg-node3
postgres

📦 第五步:编译安装 repmgr 5.4.1

5.1 下载源码(所有节点)

cd /tmp
wget https://repmgr.org/download/repmgr-5.4.1.tar.gz
tar -xzf repmgr-5.4.1.tar.gz
cd repmgr-5.4.1

✅ 预期输出:

ls -lh repmgr-5.4.1.tar.gz
-rw-r--r-- 1 root root 1.2M Apr  2 10:00 repmgr-5.4.1.tar.gz

5.2 编译安装

# 配置编译选项(指定 PostgreSQL 路径)
./configure --prefix=/home/postgres/opt/repmgr \
    --pg-bindir=/home/postgres/opt/pgsql/bin \
    --pg-libdir=/home/postgres/opt/pgsql/lib \
    --pg-includedir=/home/postgres/opt/pgsql/include

# 编译
make -j$(nproc)

# 安装
sudo make install

✅ 预期输出:

configure: creating ./config.status
config.status: creating GNUmakefile
...
/usr/bin/install -c -m 755 repmgr '/home/postgres/opt/repmgr/bin'
/usr/bin/install -c -m 755 repmgrd '/home/postgres/opt/repmgr/bin'

5.3 验证安装

repmgr --version

✅ 预期输出:

repmgr 5.4.1

🔐 第六步:创建 repmgr 用户和数据库

6.1 在主节点(192.168.118.10)执行

sudo su - postgres

# 创建 repmgr 用户(使用强密码)
psql -c "CREATE USER repmgr WITH REPLICATION LOGIN PASSWORD 'R3pMgr@2026!Secure#Prod';"

# 创建 repmgr 数据库
createdb -O repmgr repmgr

# 授予权限
psql -d repmgr -c "GRANT ALL ON DATABASE repmgr TO repmgr;"

# 验证用户创建
psql -c "\du repmgr"

✅ 预期输出:

CREATE ROLE
CREATE DATABASE
GRANT
                                   List of roles
 Role name |                         Attributes                         
-----------+------------------------------------------------------------
 repmgr    | Replication

⚙️ 第七步:配置 repmgr

7.1 创建 repmgr 配置文件(所有节点)

sudo su - postgres
vim /home/postgres/opt/repmgr/repmgr.conf

主节点(192.168.118.10)配置:

# ==================== 集群配置 ====================
cluster = pg_ha_cluster
node = 1
node_name = 'pg-node1'
node_type = 'primary'

# ==================== 连接配置 ====================
conninfo = 'host=192.168.118.10 user=repmgr password=R3pMgr@2026!Secure#Prod dbname=repmgr connect_timeout=5'

# ==================== PostgreSQL 路径配置 ====================
pg_bindir = '/home/postgres/opt/pgsql/bin'
pg_data = '/home/postgres/opt/data'

# ==================== 复制配置 ====================
repl_user = 'repmgr'
repl_password = 'R3pMgr@2026!Secure#Prod'
use_replication_slots = 1

# ==================== 日志配置 ====================
log_level = INFO
log_file = '/home/postgres/opt/log/repmgr/repmgr.log'
log_connections = on

# ==================== 监控配置 ====================
monitor_interval_secs = 10
reconnect_interval_secs = 10
ping_interval_secs = 5
ping_timeout_secs = 5

# ==================== 故障切换配置 ====================
failover = 'automatic'
failover_validation_command = '/home/postgres/opt/repmgr/scripts/validate_failover.sh'

# ==================== 备份配置 ====================
backup_method = 'pg_basebackup'

备节点 1(192.168.118.11)配置:

cluster = pg_ha_cluster
node = 2
node_name = 'pg-node2'
node_type = 'standby'
conninfo = 'host=192.168.118.11 user=repmgr password=R3pMgr@2026!Secure#Prod dbname=repmgr connect_timeout=5'
# 其他配置与主节点相同

备节点 2(192.168.118.12)配置:

cluster = pg_ha_cluster
node = 3
node_name = 'pg-node3'
node_type = 'standby'
conninfo = 'host=192.168.118.12 user=repmgr password=R3pMgr@2026!Secure#Prod dbname=repmgr connect_timeout=5'
# 其他配置与主节点相同

7.2 创建日志目录和验证脚本

# 创建日志目录
mkdir -p /home/postgres/opt/log/repmgr
mkdir -p /home/postgres/opt/repmgr/scripts

# 创建故障切换验证脚本
vim /home/postgres/opt/repmgr/scripts/validate_failover.sh
#!/bin/bash
# repmgr 故障切换验证脚本
# 参数:$1 = 新主节点 IP

NEW_PRIMARY=$1

# 检查新主节点是否可连接
/home/postgres/opt/pgsql/bin/psql -h $NEW_PRIMARY -U repmgr -d repmgr -c "SELECT 1;" > /dev/null 2>&1

if [ $? -eq 0 ]; then
    echo "$(date): Failover validation successful for $NEW_PRIMARY" >> /home/postgres/opt/log/repmgr/failover_validation.log
    exit 0
else
    echo "$(date): Failover validation FAILED for $NEW_PRIMARY" >> /home/postgres/opt/log/repmgr/failover_validation.log
    exit 1
fi
# 设置权限
chmod +x /home/postgres/opt/repmgr/scripts/validate_failover.sh
chown -R postgres:postgres /home/postgres/opt/repmgr

🚀 第八步:初始化 repmgr 集群

8.1 在主节点注册第一个节点

repmgr primary register --force

✅ 预期输出:

NOTICE: using provided configuration file "/home/postgres/opt/repmgr/repmgr.conf"
NOTICE: connecting to source node "pg-node1" (host=192.168.118.10 port=5432 user=repmgr database=repmgr)
NOTICE: successfully registered primary node "pg-node1" (ID: 1)
INFO: node registered successfully

8.2 在备节点克隆并注册

在节点 2(192.168.118.11)执行:

sudo su - postgres

# 停止 PostgreSQL
pg_ctl -D /home/postgres/opt/data stop

# 克隆主节点数据
repmgr standby clone -h 192.168.118.10 -U repmgr -d repmgr --fast-checkpoint --force

# 注册备节点
repmgr standby register

# 启动 PostgreSQL
pg_ctl -D /home/postgres/opt/data start

✅ 预期输出:

NOTICE: destination directory "/home/postgres/opt/data" provided
INFO: connecting to source node "pg-node1" (host=192.168.118.10 port=5432 user=repmgr database=repmgr)
INFO: executing pg_basebackup
NOTICE: cloning completed
NOTICE: standby node "pg-node2" (ID: 2) successfully registered
INFO: node registered successfully

在节点 3(192.168.118.12)执行相同操作:

sudo su - postgres
pg_ctl -D /home/postgres/opt/data stop
repmgr standby clone -h 192.168.118.10 -U repmgr -d repmgr --fast-checkpoint --force
repmgr standby register
pg_ctl -D /home/postgres/opt/data start

8.3 验证集群状态

repmgr cluster show

✅ 预期输出:

 ID | Name        | Role    | Status        | Upstream    | Location
----+-------------+---------+---------------+-------------+----------
 1  | pg-node1    | primary | * running     |             | default
 2  | pg-node2    | standby |   streaming   | pg-node1    | default
 3  | pg-node3    | standby |   streaming   | pg-node1    | default

8.4 验证复制状态

psql -c "SELECT client_addr, state, sync_state, replay_lsn FROM pg_stat_replication;"

✅ 预期输出:

  client_addr   |   state   | sync_state | replay_lsn 
----------------+-----------+------------+------------
 192.168.118.11 | streaming | async      | 0/3000000
 192.168.118.12 | streaming | async      | 0/3000000

🔄 第九步:手动切换主备节点(Switchover)

9.1 查看当前集群状态

repmgr cluster show

✅ 预期输出:

 ID | Name        | Role    | Status        | Upstream    | Location
----+-------------+---------+---------------+-------------+----------
 1  | pg-node1    | primary | * running     |             | default
 2  | pg-node2    | standby |   streaming   | pg-node1    | default
 3  | pg-node3    | standby |   streaming   | pg-node1    | default

9.2 执行手动切换

将主节点从 node1 切换到 node2:

sudo su - postgres

# 执行切换(--siblings-follow 让其他备节点跟随新主)
repmgr standby switchover --siblings-follow

✅ 预期输出:

NOTICE: executing switchover on node "pg-node2" (node 2)
NOTICE: checking switchover prerequisites
NOTICE: checking replication state
NOTICE: promoting standby to primary
NOTICE: demoting primary to standby
NOTICE: stopping primary
NOTICE: waiting for standby to catch up
NOTICE: starting former primary as new standby
NOTICE: switchover successful
INFO: new primary is "pg-node2" (node 2)

9.3 验证切换结果

repmgr cluster show

✅ 预期输出:

 ID | Name        | Role    | Status        | Upstream    | Location
----+-------------+---------+---------------+-------------+----------
 1  | pg-node1    | standby |   streaming   | pg-node2    | default
 2  | pg-node2    | primary | * running     |             | default
 3  | pg-node3    | standby |   streaming   | pg-node2    | default

验证新主节点可写:

psql -h 192.168.118.11 -U postgres -c "CREATE TABLE switchover_test (id int, created_at timestamp default now());"
psql -h 192.168.118.11 -U postgres -c "INSERT INTO switchover_test (id) VALUES (1);"
psql -h 192.168.118.11 -U postgres -c "SELECT * FROM switchover_test;"

✅ 预期输出:

CREATE TABLE
INSERT 0 1
 id |         created_at         
----+----------------------------
  1 | 2026-04-02 17:10:00.123456

验证备节点同步:

psql -h 192.168.118.10 -U postgres -c "SELECT * FROM switchover_test;"

✅ 预期输出:

 id |         created_at         
----+----------------------------
  1 | 2026-04-02 17:10:00.123456

⚡ 第十步:配置自动故障切换(Failover)

10.1 启动 repmgrd 守护进程(所有节点)

repmgrd -f /home/postgres/opt/repmgr/repmgr.conf --daemonize

# 验证进程
ps aux | grep repmgrd

✅ 预期输出:

postgres  12345  0.5  0.2  123456  7890 ?  Ss  17:00   0:00 repmgrd -f /home/postgres/opt/repmgr/repmgr.conf --daemonize

10.2 查看 repmgrd 日志

tail -f /home/postgres/opt/log/repmgr/repmgr.log

✅ 预期输出:

[2026-04-02 17:00:00] [INFO] repmgrd started
[2026-04-02 17:00:00] [INFO] monitoring cluster "pg_ha_cluster"
[2026-04-02 17:00:00] [INFO] node "pg-node1" (ID: 1) is primary

10.3 测试自动故障切换

模拟主节点故障:

# 在主节点(192.168.118.10)上停止 PostgreSQL
sudo su - postgres
pg_ctl -D /home/postgres/opt/data stop -m fast

在备节点查看日志:

tail -f /home/postgres/opt/log/repmgr/repmgr.log

✅ 预期输出:

[2026-04-02 17:15:00] [WARNING] unable to connect to primary node "pg-node1" (ID: 1)
[2026-04-02 17:15:10] [NOTICE] primary node "pg-node1" (ID: 1) is down
[2026-04-02 17:15:10] [NOTICE] initiating failover
[2026-04-02 17:15:11] [NOTICE] promoting node "pg-node2" (ID: 2) to primary
[2026-04-02 17:15:15] [NOTICE] failover successful

验证新的集群状态:

repmgr cluster show

✅ 预期输出:

 ID | Name        | Role    | Status        | Upstream    | Location
----+-------------+---------+---------------+-------------+----------
 1  | pg-node1    | -       | x failed      |             | default
 2  | pg-node2    | primary | * running     |             | default
 3  | pg-node3    | standby |   streaming   | pg-node2    | default

📊 第十一步:监控和维护

11.1 查看复制延迟

psql -c "SELECT 
    client_addr,
    pg_current_wal_lsn() AS current_lsn,
    replay_lsn,
    pg_size_pretty(pg_current_wal_lsn() - replay_lsn) AS lag,
    state,
    sync_state
FROM pg_stat_replication;"

✅ 预期输出:

  client_addr   | current_lsn |  replay_lsn  |  lag   
----------------+-------------+--------------+--------
 192.168.118.11 | 0/5000000   | 0/4FFFFC0    | 64 bytes
 192.168.118.12 | 0/5000000   | 0/4FFFFC0    | 64 bytes

11.2 添加监控脚本

vim /home/postgres/opt/repmgr/scripts/check_cluster.sh
#!/bin/bash
# repmgr 集群状态检查脚本

REPMGR_BIN=/home/postgres/opt/repmgr/bin/repmgr
REPMGR_CONF=/home/postgres/opt/repmgr/repmgr.conf
LOG_FILE=/home/postgres/opt/log/repmgr/cluster_status.log

# 检查集群状态
CLUSTER_STATUS=$($REPMGR_BIN -f $REPMGR_CONF cluster show 2>&1)

if [ $? -ne 0 ]; then
    echo "$(date): ERROR - Cluster check failed" >> $LOG_FILE
    echo "$CLUSTER_STATUS" >> $LOG_FILE
    exit 1
fi

# 检查是否有 failed 节点
FAILED_COUNT=$(echo "$CLUSTER_STATUS" | grep -c "x failed")
if [ $FAILED_COUNT -gt 0 ]; then
    echo "$(date): WARNING - $FAILED_COUNT node(s) failed" >> $LOG_FILE
fi

# 记录正常状态
echo "$(date): OK - Cluster status normal" >> $LOG_FILE
echo "$CLUSTER_STATUS" >> $LOG_FILE

设置定时任务:

crontab -e

# 每 5 分钟检查一次
*/5 * * * * /home/postgres/opt/repmgr/scripts/check_cluster.sh

🔧 第十二步:常见问题排查

12.1 备节点无法连接主节点

问题: could not connect to server: Connection refused

# 1. 检查防火墙
sudo firewall-cmd --add-port=5432/tcp --permanent
sudo firewall-cmd --reload

# 2. 检查 PostgreSQL 是否监听
netstat -tlnp | grep 5432

# 3. 检查 pg_hba.conf
cat /home/postgres/opt/data/pg_hba.conf | grep replication

# 4. 测试连接
psql -h 192.168.118.10 -U repmgr -d repmgr -c "SELECT 1;"

12.2 复制延迟过大

问题: 备节点落后主节点太多

# 1. 增加 WAL 保留
vim /home/postgres/opt/data/postgresql.conf
wal_keep_size = 10GB

# 2. 重启 PostgreSQL
pg_ctl -D /home/postgres/opt/data reload

# 3. 如果落后太多,重新克隆
pg_ctl -D /home/postgres/opt/data stop
repmgr standby clone --force
pg_ctl -D /home/postgres/opt/data start

12.3 脑裂问题

问题: 网络分区导致双主

# 1. 配置见证节点(第四台服务器)
repmgr witness create -h 192.168.118.100 -U repmgr -d repmgr

# 2. 使用 fencing 机制
# 在 repmgr.conf 中添加
fencing_command = '/home/postgres/opt/repmgr/scripts/fence_primary.sh'

12.4 Fencing 脚本示例

sudo vim /home/postgres/opt/repmgr/scripts/fence_primary.sh
#!/bin/bash
# =====================================================
# repmgr Fencing 脚本
# 用途:当检测到脑裂时,强制停止旧主节点,防止双写
# =====================================================

LOG_FILE="/home/postgres/opt/log/repmgr/fencing.log"
PG_CTL="/home/postgres/opt/pgsql/bin/pg_ctl"
PG_DATA="/home/postgres/opt/data"

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}

# 检查当前节点是否还是主节点
IS_PRIMARY=$($PG_CTL status -D $PG_DATA | grep -c "server is running")

if [ $IS_PRIMARY -eq 1 ]; then
    # 检查是否有复制连接(如果有备节点连接,说明不是脑裂)
    REPLICATION_COUNT=$(psql -t -c "SELECT count(*) FROM pg_stat_replication;" 2>/dev/null | tr -d ' ')
    
    if [ -z "$REPLICATION_COUNT" ] || [ "$REPLICATION_COUNT" -eq 0 ]; then
        # 没有备节点连接,可能是脑裂,立即停止服务
        log_message "WARNING: No replication connections detected, possible split-brain. Stopping PostgreSQL..."
        
        # 立即停止 PostgreSQL(不等待事务完成)
        $PG_CTL stop -D $PG_DATA -m immediate
        
        if [ $? -eq 0 ]; then
            log_message "SUCCESS: PostgreSQL stopped to prevent split-brain"
            exit 0
        else
            log_message "ERROR: Failed to stop PostgreSQL"
            exit 1
        fi
    else
        log_message "INFO: $REPLICATION_COUNT replication connection(s) active, no action needed"
        exit 0
    fi
else
    log_message "INFO: This node is not primary, no fencing required"
    exit 0
fi

设置权限:

chmod +x /home/postgres/opt/repmgr/scripts/fence_primary.sh
chown postgres:postgres /home/postgres/opt/repmgr/scripts/fence_primary.sh

测试脚本:

# 手动执行测试
sudo -u postgres /home/postgres/opt/repmgr/scripts/fence_primary.sh

# 查看日志
tail -f /home/postgres/opt/log/repmgr/fencing.log

✅ 预期输出:

2026-04-02 17:45:00 - INFO: This node is not primary, no fencing required

⚠️ 重要说明:

  1. fencing 是最后手段 - 只在检测到脑裂时触发
  2. 谨慎使用 - 误触发会导致服务中断
  3. 配合见证节点 - 单独使用 fencing 风险较高
  4. 定期测试 - 确保脚本在关键时刻能正常工作

📝 总结

手动切换 vs 自动切换

特性 手动切换 自动切换
安全性 高(人工确认) 中(可能误切换)
响应时间 分钟级 秒级
适用场景 计划内维护 故障应急
风险 中(脑裂风险)
命令 repmgr standby switchover 自动触发

生产环境建议

  1. 使用见证节点 - 防止脑裂(至少 3 节点 +1 见证)
  2. 配置监控告警 - 复制延迟超过 100MB 自动通知
  3. 定期演练 - 每季度至少一次切换演练
  4. 备份策略 - 结合 pg_basebackup + WAL 归档
  5. fencing 机制 - 防止旧主节点恢复后造成冲突
  6. 日志轮转 - 配置 log_rotation_age 和 log_rotation_size

📚 系列文章

  1. PostgreSQL 复制配置参数详解
  2. PostgreSQL 流复制主从切换
  3. PostgreSQL 备份恢复实战
  4. PostgreSQL 高可用:repmgr 搭建与切换(本文)
  5. PostgreSQL 监控告警:复制延迟超过 100MB 自动通知
  6. PostgreSQL 性能调优:内存、连接数、WAL 参数配置
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐