PostgreSQL 复制教程

1. 核心概念

PostgreSQL 复制是指将数据从一个 PostgreSQL 数据库服务器(主服务器)复制到一个或多个 PostgreSQL 数据库服务器(从服务器)的过程。通过复制,可以提高系统的可用性、可靠性和扩展性。

1.1 复制类型

PostgreSQL 支持多种复制方式:

  • 物理复制(流复制):基于 WAL(Write-Ahead Log)日志的复制,复制整个数据库集群的二进制数据
  • 逻辑复制:基于逻辑变更的复制,可以复制特定的表或数据库
  • 级联复制:从服务器作为其他从服务器的主服务器
  • 同步复制:主服务器等待从服务器确认后再提交事务
  • 异步复制:主服务器无需等待从服务器确认即可提交事务

1.2 复制架构

  • 主服务器(Primary):负责处理所有写操作和读操作(默认情况下)
  • 从服务器(Standby):复制主服务器的数据,可用于处理读操作
  • WAL 日志:Write-Ahead Log,记录所有数据库变更的日志
  • WAL 发送器:主服务器上的进程,负责发送 WAL 日志到从服务器
  • WAL 接收器:从服务器上的进程,负责接收 WAL 日志并应用到本地数据库

1.3 复制原理

1.3.1 物理复制原理

  1. 主服务器执行写操作,将变更记录到 WAL 日志
  2. WAL 发送器将 WAL 日志发送到从服务器
  3. 从服务器的 WAL 接收器接收 WAL 日志并写入本地 WAL 日志
  4. 从服务器应用 WAL 日志中的变更,保持数据与主服务器一致

1.3.2 逻辑复制原理

  1. 主服务器执行写操作,将变更记录到 WAL 日志
  2. 逻辑解码插件将 WAL 日志解码为逻辑变更
  3. 发布者将逻辑变更发送到订阅者
  4. 订阅者应用逻辑变更,保持数据与主服务器一致

1.4 复制模式

  • 异步复制:主服务器无需等待从服务器确认,性能更好,但可能会丢失数据
  • 同步复制:主服务器等待从服务器确认后再提交事务,数据安全性更高,但性能可能会受到影响
  • 半同步复制:主服务器等待至少一个从服务器确认后再提交事务,平衡了性能和数据安全性

2. 安装与配置

2.1 安装 PostgreSQL

2.1.1 在 Ubuntu/Debian 系统上安装

# 更新包列表
sudo apt update

# 安装 PostgreSQL
sudo apt install -y postgresql postgresql-contrib

# 启动 PostgreSQL 服务
sudo systemctl start postgresql

# 设置 PostgreSQL 服务开机自启
sudo systemctl enable postgresql

2.1.2 在 CentOS/RHEL 系统上安装

# 安装 PostgreSQL 仓库
sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# 安装 PostgreSQL
sudo yum install -y postgresql14-server postgresql14-contrib

# 初始化数据库
sudo /usr/pgsql-14/bin/postgresql-14-setup initdb

# 启动 PostgreSQL 服务
sudo systemctl start postgresql-14

# 设置 PostgreSQL 服务开机自启
sudo systemctl enable postgresql-14

2.1.3 从源码编译安装

# 下载 PostgreSQL 源码
wget https://ftp.postgresql.org/pub/source/v14.5/postgresql-14.5.tar.gz
tar -xzf postgresql-14.5.tar.gz
cd postgresql-14.5

# 安装依赖
sudo apt install -y build-essential libreadline-dev zlib1g-dev libssl-dev

# 配置编译选项
./configure --prefix=/usr/local/pgsql --with-openssl

# 编译并安装
make
sudo make install

# 创建用户和数据目录
sudo useradd -m postgres
sudo mkdir -p /usr/local/pgsql/data
sudo chown -R postgres:postgres /usr/local/pgsql

# 切换到 postgres 用户并初始化数据库
sudo su - postgres
/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data

# 启动 PostgreSQL 服务
/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data start

# 添加 PostgreSQL 到 PATH
echo 'export PATH=/usr/local/pgsql/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

2.2 配置物理复制

2.2.1 配置主服务器

  1. 修改 postgresql.conf 文件

    sudo nano /etc/postgresql/14/main/postgresql.conf

    添加或修改以下配置:

    # 监听地址
    listen_addresses = '*'
    
    # WAL 级别
    wal_level = replica
    
    # 最大 WAL 发送进程数
    max_wal_senders = 10
    
    # 最大复制槽数量
    max_replication_slots = 10
    
    #  wal_keep_size = 2GB
    
    # 同步复制配置(可选)
    synchronous_commit = on
    synchronous_standby_names = '*'
  2. 修改 pg_hba.conf 文件,添加复制用户的访问权限:

    sudo nano /etc/postgresql/14/main/pg_hba.conf

    添加以下配置:

    # 允许复制用户从从服务器访问
    host    replication     replica     192.168.1.0/24        md5
  3. 创建复制用户

    sudo -u postgres psql
    
    -- 创建复制用户
    CREATE USER replica WITH REPLICATION PASSWORD 'your_strong_password';
    
    -- 退出
    \q
  4. 重启主服务器

    sudo systemctl restart postgresql

2.2.2 配置从服务器

  1. 停止从服务器

    sudo systemctl stop postgresql
  2. 清空从服务器的数据目录

    sudo rm -rf /var/lib/postgresql/14/main/*
  3. 使用 pg_basebackup 从主服务器复制数据

    sudo -u postgres pg_basebackup -h 主服务器IP -U replica -D /var/lib/postgresql/14/main -P -v -R

    参数说明:

    • -h:主服务器 IP 地址
    • -U:复制用户
    • -D:从服务器数据目录
    • -P:显示进度
    • -v:详细输出
    • -R:自动生成 recovery.conf 文件
  4. 修改从服务器的 postgresql.conf 文件

    sudo nano /etc/postgresql/14/main/postgresql.conf

    添加或修改以下配置:

    # 监听地址
    listen_addresses = '*'
    
    # 最大 WAL 接收进程数
    max_worker_processes = 10
    
    # 热备模式
    hot_standby = on
    
    # 允许从服务器接受连接
    max_connections = 100
  5. 修改从服务器的 recovery.conf 文件(PostgreSQL 12+ 版本为 postgresql.auto.conf):

    sudo nano /var/lib/postgresql/14/main/recovery.conf

    确保以下配置正确:

    standby_mode = 'on'
    primary_conninfo = 'host=主服务器IP port=5432 user=replica password=your_strong_password'
    trigger_file = '/var/lib/postgresql/14/main/trigger'
  6. 启动从服务器

    sudo systemctl start postgresql

2.2.3 验证复制状态

  1. 在主服务器上查看复制状态

    sudo -u postgres psql
    
    -- 查看复制槽状态
    SELECT * FROM pg_replication_slots;
    
    -- 查看 WAL 发送器状态
    SELECT * FROM pg_stat_replication;
    
    -- 退出
    \q
  2. 在从服务器上查看复制状态

    sudo -u postgres psql
    
    -- 查看从服务器状态
    SELECT * FROM pg_stat_wal_receiver;
    
    -- 查看复制延迟
    SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), pg_last_wal_receive_lsn()) AS lag_bytes;
    
    -- 退出
    \q

2.3 配置逻辑复制

2.3.1 配置主服务器

  1. 修改 postgresql.conf 文件

    sudo nano /etc/postgresql/14/main/postgresql.conf

    添加或修改以下配置:

    # WAL 级别
    wal_level = logical
    
    # 最大 WAL 发送进程数
    max_wal_senders = 10
    
    # 最大复制槽数量
    max_replication_slots = 10
    
    # 最大逻辑复制工作进程数
    max_worker_processes = 10
    max_logical_replication_workers = 4
  2. 重启主服务器

    sudo systemctl restart postgresql
  3. 创建发布

    sudo -u postgres psql
    
    -- 创建发布
    CREATE PUBLICATION my_publication FOR TABLE users, products;
    
    -- 或发布所有表
    -- CREATE PUBLICATION my_publication FOR ALL TABLES;
    
    -- 退出
    \q

2.3.2 配置从服务器

  1. 创建订阅

    sudo -u postgres psql
    
    -- 创建订阅
    CREATE SUBSCRIPTION my_subscription
    CONNECTION 'host=主服务器IP port=5432 dbname=postgres user=replica password=your_strong_password'
    PUBLICATION my_publication;
    
    -- 退出
    \q
  2. 验证逻辑复制状态

    sudo -u postgres psql
    
    -- 查看订阅状态
    SELECT * FROM pg_stat_subscription;
    
    -- 退出
    \q

3. 基本使用

3.1 连接到 PostgreSQL

3.1.1 使用 psql 命令行工具

# 连接到主服务器
psql -h 主服务器IP -U postgres -d 数据库名

# 连接到从服务器
psql -h 从服务器IP -U postgres -d 数据库名

3.1.2 使用 Node.js 客户端

const { Client } = require('pg');

// 连接到主服务器
const primaryClient = new Client({
  host: '主服务器IP',
  port: 5432,
  user: 'postgres',
  password: 'your_strong_password',
  database: 'postgres'
});

// 连接到从服务器
const standbyClient = new Client({
  host: '从服务器IP',
  port: 5432,
  user: 'postgres',
  password: 'your_strong_password',
  database: 'postgres'
});

// 连接到数据库
async function connect() {
  try {
    await primaryClient.connect();
    await standbyClient.connect();
    console.log('Connected to PostgreSQL servers');
  } catch (err) {
    console.error('Error connecting to PostgreSQL:', err);
  }
}

connect();

3.2 数据操作

3.2.1 写操作

写操作应该在主服务器上执行:

# 连接到主服务器
psql -h 主服务器IP -U postgres -d testdb

-- 创建表
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(100) UNIQUE
);

-- 插入数据
INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com');

-- 更新数据
UPDATE users SET name = '李四' WHERE id = 1;

-- 删除数据
DELETE FROM users WHERE id = 1;

-- 退出
\q

3.2.2 读操作

读操作可以在主服务器或从服务器上执行:

# 连接到从服务器
psql -h 从服务器IP -U postgres -d testdb

-- 查询数据
SELECT * FROM users;

-- 退出
\q

3.3 管理复制

3.3.1 查看复制状态

# 连接到主服务器
psql -h 主服务器IP -U postgres

-- 查看 WAL 发送器状态
SELECT * FROM pg_stat_replication;

-- 查看复制槽状态
SELECT * FROM pg_replication_slots;

-- 退出
\q

# 连接到从服务器
psql -h 从服务器IP -U postgres

-- 查看 WAL 接收器状态
SELECT * FROM pg_stat_wal_receiver;

-- 查看复制延迟
SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), pg_last_wal_receive_lsn()) AS lag_bytes;

-- 退出
\q

3.3.2 执行故障转移

当主服务器发生故障时,需要将从服务器提升为新的主服务器:

  1. 在从服务器上创建触发文件

    sudo -u postgres touch /var/lib/postgresql/14/main/trigger
  2. 验证从服务器是否已提升为主服务器

    psql -h 从服务器IP -U postgres
    
    -- 查看服务器状态
    SELECT pg_is_in_recovery();
    
    -- 退出
    \q

    如果返回 false,表示从服务器已成功提升为主服务器。

  3. 将其他从服务器重新指向新的主服务器

    # 修改 recovery.conf 文件
    sudo nano /var/lib/postgresql/14/main/recovery.conf
    
    # 修改 primary_conninfo
    primary_conninfo = 'host=新主服务器IP port=5432 user=replica password=your_strong_password'
    
    # 重启从服务器
    sudo systemctl restart postgresql

3.3.3 重新同步从服务器

当从服务器与主服务器数据不一致时,需要重新同步:

  1. 停止从服务器

    sudo systemctl stop postgresql
  2. 清空从服务器的数据目录

    sudo rm -rf /var/lib/postgresql/14/main/*
  3. 从主服务器复制数据

    sudo -u postgres pg_basebackup -h 主服务器IP -U replica -D /var/lib/postgresql/14/main -P -v -R
  4. 启动从服务器

    sudo systemctl start postgresql

4. 高级功能

4.1 同步复制

同步复制可以确保数据的安全性,但会影响性能:

4.1.1 配置同步复制

  1. 修改主服务器的 postgresql.conf 文件

    sudo nano /etc/postgresql/14/main/postgresql.conf

    添加或修改以下配置:

    # 同步复制
    synchronous_commit = on
    
    # 同步从服务器名称
    synchronous_standby_names = 'standby1,standby2'
  2. 修改从服务器的 recovery.conf 文件,添加应用名称:

    sudo nano /var/lib/postgresql/14/main/recovery.conf

    添加以下配置:

    primary_conninfo = 'host=主服务器IP port=5432 user=replica password=your_strong_password application_name=standby1'
  3. 重启主服务器和从服务器

    sudo systemctl restart postgresql

4.1.2 验证同步复制

# 连接到主服务器
psql -h 主服务器IP -U postgres

-- 查看复制状态
SELECT * FROM pg_stat_replication WHERE sync_state = 'sync';

-- 退出
\q

4.2 逻辑复制

逻辑复制可以复制特定的表或数据库,更加灵活:

4.2.1 创建发布

# 连接到主服务器
psql -h 主服务器IP -U postgres

-- 创建发布
CREATE PUBLICATION my_publication FOR TABLE users, products;

-- 查看发布
SELECT * FROM pg_publication;

-- 查看发布的表
SELECT * FROM pg_publication_tables;

-- 退出
\q

4.2.2 创建订阅

# 连接到从服务器
psql -h 从服务器IP -U postgres

-- 创建订阅
CREATE SUBSCRIPTION my_subscription
CONNECTION 'host=主服务器IP port=5432 dbname=postgres user=replica password=your_strong_password'
PUBLICATION my_publication;

-- 查看订阅
SELECT * FROM pg_subscription;

-- 查看订阅状态
SELECT * FROM pg_stat_subscription;

-- 退出
\q

4.2.3 管理发布和订阅

# 连接到主服务器
psql -h 主服务器IP -U postgres

-- 添加表到发布
ALTER PUBLICATION my_publication ADD TABLE orders;

-- 从发布中移除表
ALTER PUBLICATION my_publication DROP TABLE products;

-- 删除发布
DROP PUBLICATION my_publication;

-- 退出
\q

# 连接到从服务器
psql -h 从服务器IP -U postgres

-- 刷新订阅
ALTER SUBSCRIPTION my_subscription REFRESH PUBLICATION;

-- 禁用订阅
ALTER SUBSCRIPTION my_subscription DISABLE;

-- 启用订阅
ALTER SUBSCRIPTION my_subscription ENABLE;

-- 删除订阅
DROP SUBSCRIPTION my_subscription;

-- 退出
\q

4.3 级联复制

级联复制可以减轻主服务器的负担,适用于大型集群:

4.3.1 配置级联复制

  1. 配置主服务器

    sudo nano /etc/postgresql/14/main/postgresql.conf
    
    # 添加以下配置
    wal_level = replica
    max_wal_senders = 10
    max_replication_slots = 10
  2. 配置第一级从服务器

    sudo nano /var/lib/postgresql/14/main/recovery.conf
    
    # 添加以下配置
    standby_mode = 'on'
    primary_conninfo = 'host=主服务器IP port=5432 user=replica password=your_strong_password'
    primary_slot_name = 'slot1'
    hot_standby = on
    max_wal_senders = 10
    max_replication_slots = 10
  3. 配置第二级从服务器

    sudo nano /var/lib/postgresql/14/main/recovery.conf
    
    # 添加以下配置
    standby_mode = 'on'
    primary_conninfo = 'host=第一级从服务器IP port=5432 user=replica password=your_strong_password'
    primary_slot_name = 'slot2'
    hot_standby = on
  4. 在主服务器上创建复制槽

    psql -h 主服务器IP -U postgres
    
    -- 创建复制槽
    SELECT * FROM pg_create_physical_replication_slot('slot1');
    
    -- 退出
    \q
  5. 在第一级从服务器上创建复制槽

    psql -h 第一级从服务器IP -U postgres
    
    -- 创建复制槽
    SELECT * FROM pg_create_physical_replication_slot('slot2');
    
    -- 退出
    \q

4.4 复制监控

4.4.1 使用 pg_stat_replication 视图

# 连接到主服务器
psql -h 主服务器IP -U postgres

-- 查看复制状态
SELECT 
  client_addr,
  application_name,
  state,
  sync_state,
  write_lag,
  flush_lag,
  replay_lag
FROM pg_stat_replication;

-- 退出
\q

4.4.2 使用 pg_stat_wal_receiver 视图

# 连接到从服务器
psql -h 从服务器IP -U postgres

-- 查看 WAL 接收器状态
SELECT 
  status,
  receive_start_lsn,
  received_lsn,
  last_msg_send_time,
  last_msg_receipt_time,
  latest_end_lsn,
  latest_end_time
FROM pg_stat_wal_receiver;

-- 退出
\q

4.4.3 使用 Prometheus 和 Grafana 监控

  1. 安装 PostgreSQL Exporter

    wget https://github.com/prometheus-community/postgres_exporter/releases/download/v0.13.2/postgres_exporter-0.13.2.linux-amd64.tar.gz
    tar -xzf postgres_exporter-0.13.2.linux-amd64.tar.gz
    cd postgres_exporter-0.13.2.linux-amd64
  2. 创建监控用户

    psql -h 主服务器IP -U postgres
    
    -- 创建监控用户
    CREATE USER pg_exporter WITH PASSWORD 'your_strong_password';
    GRANT CONNECT ON DATABASE postgres TO pg_exporter;
    GRANT SELECT ON pg_stat_database TO pg_exporter;
    GRANT SELECT ON pg_stat_replication TO pg_exporter;
    GRANT SELECT ON pg_stat_wal_receiver TO pg_exporter;
    
    -- 退出
    \q
  3. 创建 .pgpass 文件

    echo '*:*:*:pg_exporter:your_strong_password' > .pgpass
    chmod 600 .pgpass
  4. 启动 PostgreSQL Exporter

    export DATA_SOURCE_NAME="postgresql://pg_exporter:your_strong_password@localhost:5432/postgres"
    ./postgres_exporter
  5. 配置 Prometheus,添加 PostgreSQL Exporter 作为目标。

  6. 配置 Grafana,导入 PostgreSQL 监控面板。

4.5 备份与恢复

4.5.1 使用 pg_dump 备份

# 备份整个数据库
pg_dump -h 主服务器IP -U postgres -d testdb -F c -f testdb.backup

# 备份特定表
pg_dump -h 主服务器IP -U postgres -d testdb -t users -F c -f users.backup

# 备份 schema
pg_dump -h 主服务器IP -U postgres -d testdb -s -F c -f schema.backup

4.5.2 使用 pg_restore 恢复

# 恢复整个数据库
pg_restore -h 主服务器IP -U postgres -d testdb -F c testdb.backup

# 恢复特定表
pg_restore -h 主服务器IP -U postgres -d testdb -t users -F c users.backup

# 恢复 schema
pg_restore -h 主服务器IP -U postgres -d testdb -s -F c schema.backup

4.5.3 使用 WAL 归档进行时间点恢复

  1. 配置 WAL 归档

    sudo nano /etc/postgresql/14/main/postgresql.conf
    
    # 添加以下配置
    wal_level = replica
    archive_mode = on
    archive_command = 'cp %p /path/to/archive/%f'
  2. 创建归档目录

    sudo mkdir -p /path/to/archive
    sudo chown -R postgres:postgres /path/to/archive
  3. 执行基础备份

    sudo -u postgres pg_basebackup -h 主服务器IP -U replica -D /path/to/backup -P -v
  4. 执行时间点恢复

    # 修改 recovery.conf 文件
    sudo nano /path/to/backup/recovery.conf
    
    # 添加以下配置
    restore_command = 'cp /path/to/archive/%f %p'
    recovery_target_time = '2023-01-01 12:00:00'
    
    # 启动服务器
    sudo -u postgres pg_ctl -D /path/to/backup start

5. 最佳实践

5.1 部署建议

  • 服务器配置:主服务器和从服务器应使用相同的硬件配置,确保性能一致
  • 网络配置:确保主服务器和从服务器之间网络畅通,网络延迟应尽可能低
  • 存储配置:使用 SSD 存储提高性能,特别是对于写密集型应用
  • 防火墙:确保开放必要的端口(默认 5432)
  • 部署位置:将主服务器和从服务器部署在不同的物理机器或虚拟机上,避免单点故障
  • 地理分布:对于关键应用,考虑跨数据中心部署,提高灾备能力

5.2 性能优化

  • WAL 配置:根据实际需求调整 wal_levelmax_wal_senders 等参数
  • 内存配置:为 PostgreSQL 分配足够的内存,建议将 shared_buffers 设置为系统内存的 25%
  • 工作进程:根据 CPU 核心数调整 max_worker_processes 参数
  • 连接池:使用连接池管理客户端连接,避免频繁创建和销毁连接
  • 索引优化:为常用查询创建合适的索引,避免全表扫描
  • 分区表:对于大型表,使用分区表提高查询性能
  • 读写分离:将读操作分发到从服务器,提高系统整体性能

5.3 安全配置

  • 启用认证:启用 PostgreSQL 的认证机制,设置强密码
  • 限制访问:通过 pg_hba.conf 文件限制只接受特定 IP 的连接
  • 使用 TLS:在生产环境中启用 TLS 加密
  • 定期更新:及时更新 PostgreSQL 版本,修复安全漏洞
  • 权限管理:根据最小权限原则,为不同用户分配适当的角色
  • 审计日志:启用审计日志,记录重要操作

5.4 监控与维护

  • 监控指标:监控服务器状态、复制延迟、内存使用、CPU 使用率、磁盘空间等指标
  • 日志分析:定期分析 PostgreSQL 日志,及时发现问题
  • 定期备份:制定合理的备份策略,确保数据安全
  • 容量规划:根据数据增长趋势,提前规划存储容量
  • 健康检查:定期执行 VACUUMANALYZE 操作,维护数据库健康状态
  • 索引维护:定期重建索引,提高查询性能

6. 故障排查

6.1 常见问题

  • 复制延迟过高:检查网络连接,确认从服务器硬件配置是否足够,查看是否有大量写操作
  • 复制中断:检查网络连接,查看主服务器和从服务器的日志文件,确认复制用户权限是否正确
  • 从服务器无法启动:检查 recovery.conf 文件配置是否正确,查看从服务器的日志文件
  • 主服务器故障:执行故障转移,将从服务器提升为新的主服务器
  • 数据不一致:重新同步从服务器,确保数据与主服务器一致
  • 性能问题:检查索引使用情况,确认硬件配置是否足够,查看是否有慢查询

6.2 排查工具

  • psql 命令行工具:用于执行管理命令和查看状态
  • pgAdmin:图形化管理工具,提供可视化监控
  • PostgreSQL Exporter + Prometheus + Grafana:用于监控 PostgreSQL 性能指标
  • 日志文件:包含详细的操作和错误信息
  • pg_stat_ 视图*:用于查看 PostgreSQL 的各种状态信息

6.3 示例排查过程

问题:复制延迟过高

排查步骤

  1. 查看复制状态

    psql -h 主服务器IP -U postgres
    
    -- 查看复制状态
    SELECT 
      client_addr,
      application_name,
      state,
      sync_state,
      write_lag,
      flush_lag,
      replay_lag
    FROM pg_stat_replication;
    
    -- 退出
    \q
  2. 查看从服务器状态

    psql -h 从服务器IP -U postgres
    
    -- 查看 WAL 接收器状态
    SELECT * FROM pg_stat_wal_receiver;
    
    -- 查看复制延迟
    SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), pg_last_wal_receive_lsn()) AS lag_bytes;
    
    -- 退出
    \q
  3. 检查网络连接

    ping 主服务器IP
    traceroute 主服务器IP
    iperf3 -c 主服务器IP
  4. 检查主服务器负载

    top
    iostat -x
    vmstat
  5. 检查从服务器负载

    top
    iostat -x
    vmstat
  6. 查看日志文件

    sudo tail -f /var/log/postgresql/postgresql-14-main.log
  7. 解决方法

    • 增加从服务器硬件资源
    • 优化主服务器写操作
    • 检查网络连接,减少网络延迟
    • 调整 PostgreSQL 配置参数,如 max_wal_senderswork_mem

7. 实用案例

7.1 构建高可用 PostgreSQL 集群

场景:构建一个高可用的 PostgreSQL 集群,用于存储企业核心数据。

解决方案

  1. 部署架构

    • 1 个主服务器
    • 2 个从服务器
    • 1 个负载均衡器(如 HAProxy)
    • 节点分布在 3 台不同的物理机器上
  2. 配置步骤

    • 在每台机器上安装 PostgreSQL
    • 配置主服务器
    • 配置从服务器
    • 配置负载均衡器
    • 配置自动故障转移
  3. 配置负载均衡器(HAProxy)

    sudo nano /etc/haproxy/haproxy.cfg

    添加以下配置:

    frontend postgresql
      bind *:5432
      mode tcp
      option tcplog
      default_backend postgresql_servers
    
    backend postgresql_servers
      mode tcp
      balance roundrobin
      option tcp-check
      tcp-check expect string PostgreSQL
      server primary 主服务器IP:5432 check
      server standby1 从服务器1IP:5432 check
      server standby2 从服务器2IP:5432 check
  4. 配置自动故障转移(使用 Patroni)

    # 安装 Patroni
    pip install patroni psycopg2-binary
    
    # 创建 Patroni 配置文件
    sudo nano /etc/patroni.yml

    添加以下配置:

    scope: postgres
    namespace: /db/
    name: postgresql0
    
    restapi:
      listen: 0.0.0.0:8008
      connect_address: 主服务器IP:8008
    
    etcd:
      host: etcd服务器IP:2379
    
    bootstrap:
      dcs:
        ttl: 30
        loop_wait: 10
        retry_timeout: 10
        maximum_lag_on_failover: 1048576
        postgresql:
          use_pg_rewind: true
    
      initdb:
      - encoding: UTF8
      - data-checksums
    
      pg_hba:
      - host replication replicator 127.0.0.1/32 md5
      - host replication replicator 主服务器IP/32 md5
      - host replication replicator 从服务器1IP/32 md5
      - host replication replicator 从服务器2IP/32 md5
      - host all all 0.0.0.0/0 md5
    
    postgresql:
      listen: 0.0.0.0:5432
      connect_address: 主服务器IP:5432
      data_dir: /var/lib/postgresql/14/main
      pgpass: /tmp/pgpass
      authentication:
        replication:
          username: replicator
          password: your_strong_password
        superuser:
          username: postgres
          password: your_strong_password
      parameters:
        unix_socket_directories: '/var/run/postgresql'
    
    tags:
      nofailover: false
      noloadbalance: false
      clonefrom: false
      nosync: false
  5. 启动 Patroni

    sudo -u postgres patroni /etc/patroni.yml

7.2 使用 PostgreSQL 复制实现读写分离

场景:使用 PostgreSQL 复制实现读写分离,提高系统整体性能。

解决方案

  1. 部署架构

    • 1 个主服务器
    • 2 个从服务器
    • 1 个负载均衡器(如 HAProxy)
  2. 配置负载均衡器(HAProxy)

    sudo nano /etc/haproxy/haproxy.cfg

    添加以下配置:

    # 写操作前端
    frontend postgresql_write
      bind *:5432
      mode tcp
      option tcplog
      default_backend postgresql_primary
    
    # 读操作前端
    frontend postgresql_read
      bind *:5433
      mode tcp
      option tcplog
      default_backend postgresql_standbys
    
    # 主服务器后端
    backend postgresql_primary
      mode tcp
      balance roundrobin
      option tcp-check
      tcp-check expect string PostgreSQL
      server primary 主服务器IP:5432 check
    
    # 从服务器后端
    backend postgresql_standbys
      mode tcp
      balance roundrobin
      option tcp-check
      tcp-check expect string PostgreSQL
      server standby1 从服务器1IP:5432 check
      server standby2 从服务器2IP:5432 check
  3. 客户端配置

    const { Client } = require('pg');
    
    // 写操作客户端
    const writeClient = new Client({
      host: '负载均衡器IP',
      port: 5432, // 写操作端口
      user: 'postgres',
      password: 'your_strong_password',
      database: 'testdb'
    });
    
    // 读操作客户端
    const readClient = new Client({
      host: '负载均衡器IP',
      port: 5433, // 读操作端口
      user: 'postgres',
      password: 'your_strong_password',
      database: 'testdb'
    });
    
    // 连接到数据库
    async function connect() {
      try {
        await writeClient.connect();
        await readClient.connect();
        console.log('Connected to PostgreSQL servers');
      } catch (err) {
        console.error('Error connecting to PostgreSQL:', err);
      }
    }
    
    // 写操作
    async function writeOperation() {
      try {
        const result = await writeClient.query(
          'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id',
          ['张三', 'zhangsan@example.com']
        );
        console.log('Write operation successful:', result.rows[0].id);
        return result.rows[0].id;
      } catch (err) {
        console.error('Error in write operation:', err);
        throw err;
      }
    }
    
    // 读操作
    async function readOperation(userId) {
      try {
        const result = await readClient.query(
          'SELECT * FROM users WHERE id = $1',
          [userId]
        );
        console.log('Read operation successful:', result.rows[0]);
        return result.rows[0];
      } catch (err) {
        console.error('Error in read operation:', err);
        throw err;
      }
    }
    
    // 使用示例
    async function main() {
      await connect();
      
      // 执行写操作
      const userId = await writeOperation();
      
      // 执行读操作
      const user = await readOperation(userId);
      
      // 关闭连接
      await writeClient.end();
      await readClient.end();
    }
    
    main();

7.3 使用 PostgreSQL 逻辑复制实现数据同步

场景:使用 PostgreSQL 逻辑复制实现不同数据库之间的数据同步。

解决方案

  1. 部署架构

    • 源数据库(主服务器)
    • 目标数据库(从服务器)
  2. 配置源数据库

    sudo nano /etc/postgresql/14/main/postgresql.conf
    
    # 添加以下配置
    wal_level = logical
    max_wal_senders = 10
    max_replication_slots = 10
    max_worker_processes = 10
    max_logical_replication_workers = 4
    
    # 重启源数据库
    sudo systemctl restart postgresql
    
    # 创建发布
    psql -h 源数据库IP -U postgres
    
    -- 创建发布
    CREATE PUBLICATION my_publication FOR TABLE users, products;
    
    -- 退出
    \q
  3. 配置目标数据库

    # 创建目标表
    psql -h 目标数据库IP -U postgres
    
    -- 创建表
    CREATE TABLE users (
      id SERIAL PRIMARY KEY,
      name VARCHAR(100),
      email VARCHAR(100) UNIQUE
    );
    
    CREATE TABLE products (
      id SERIAL PRIMARY KEY,
      name VARCHAR(100),
      price DECIMAL(10, 2)
    );
    
    -- 创建订阅
    CREATE SUBSCRIPTION my_subscription
    CONNECTION 'host=源数据库IP port=5432 dbname=postgres user=replica password=your_strong_password'
    PUBLICATION my_publication;
    
    -- 查看订阅状态
    SELECT * FROM pg_stat_subscription;
    
    -- 退出
    \q
  4. 验证数据同步

    # 在源数据库中插入数据
    psql -h 源数据库IP -U postgres
    
    -- 插入数据
    INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com');
    INSERT INTO products (name, price) VALUES ('产品 1', 100.00);
    
    -- 退出
    \q
    
    # 在目标数据库中查看数据
    psql -h 目标数据库IP -U postgres
    
    -- 查看数据
    SELECT * FROM users;
    SELECT * FROM products;
    
    -- 退出
    \q

8. 总结

PostgreSQL 复制是构建高可用 PostgreSQL 集群的关键技术,通过物理复制或逻辑复制,可以提高系统的可用性、可靠性和扩展性。本教程详细介绍了 PostgreSQL 复制的核心概念、部署配置、使用方法和最佳实践,帮助开发者快速掌握 PostgreSQL 复制的使用技巧,构建稳定可靠的 PostgreSQL 高可用集群。

在实际应用中,应根据具体需求和场景,选择合适的复制方式,合理配置 PostgreSQL 参数,确保系统的稳定性和性能。同时,定期进行集群维护和监控,及时发现和解决问题,确保 PostgreSQL 集群持续稳定运行。

« 上一篇 MongoDB Replica Set 中文教程