Skip to content

PostgreSQL 从单机到集群

随着业务的发展,单机 PostgreSQL 数据库可能无法满足日益增长的性能和可用性需求。将单机数据库迁移到集群架构是提高系统可用性、性能和可扩展性的重要手段。本文将详细介绍 PostgreSQL 从单机到集群的迁移过程、架构选择、实施步骤和最佳实践。

集群架构选择

1. 主从复制架构

特点

  • 一主多从架构,主库负责读写,从库负责只读查询
  • 基于 WAL 日志的异步或同步复制
  • 从库可用于故障转移和读写分离
  • 实现简单,维护成本低

适用场景

  • 提高读性能(通过读写分离)
  • 提高可用性(通过故障转移)
  • 数据备份和灾备

2. 流式复制集群

特点

  • 基于 PostgreSQL 内置的流复制机制
  • 支持同步复制和异步复制
  • 可配合第三方工具实现自动故障转移
  • 支持级联复制(从库作为其他从库的主库)

适用场景

  • 高可用性要求较高的场景
  • 数据一致性要求较高的场景
  • 需要自动故障转移的场景

3. 逻辑复制集群

特点

  • 基于 PostgreSQL 10+ 内置的逻辑复制机制
  • 支持表级复制和选择性复制
  • 支持跨版本复制
  • 支持多主复制(通过双向逻辑复制)

适用场景

  • 需要选择性复制的场景
  • 跨版本复制场景
  • 多主复制场景
  • 数据分片场景

4. 分片集群

特点

  • 将数据分散存储在多个节点上
  • 每个节点只存储部分数据
  • 支持水平扩展
  • 需要中间件或扩展支持

适用场景

  • 超大数据量场景(TB 或 PB 级别)
  • 高并发写入场景
  • 需要水平扩展的场景

5. 常用集群解决方案

解决方案类型特点适用场景
PostgreSQL 原生流复制主从复制官方支持,实现简单基本高可用性需求
Patroni + etcd高可用集群自动故障转移,负载均衡企业级高可用性需求
pgpool-II中间件负载均衡,连接池,故障转移读写分离,负载均衡需求
Citus分片集群水平扩展,分布式查询超大数据量,高并发需求
TimescaleDB时序集群时序数据优化,水平扩展时序数据场景

迁移前准备

环境准备

  • 源环境检查

    sql
    -- 检查源数据库版本
    SELECT version();
    
    -- 检查数据库大小
    SELECT datname, pg_database_size(datname) / 1024 / 1024 AS size_mb
    FROM pg_database;
    
    -- 检查WAL配置
    SHOW wal_level;
    SHOW archive_mode;
    SHOW archive_command;
  • 目标环境准备

    • 准备至少3台服务器(1主2从或3主)
    • 安装与源数据库版本相同的 PostgreSQL
    • 配置相同的参数(postgresql.conf、pg_hba.conf等)
    • 配置服务器之间的SSH免密登录
    • 配置防火墙规则,允许服务器之间的通信

备份准备

  • 全量备份:对源数据库进行全量备份,确保数据安全

    bash
    pg_basebackup -h source_host -p 5432 -U repl -D /path/to/backup -F t -z -P
  • 增量备份:确保WAL日志正常归档,以便进行增量恢复

    sql
    -- 启用WAL归档
    ALTER SYSTEM SET archive_mode = on;
    ALTER SYSTEM SET archive_command = 'cp %p /path/to/archive/%f';
    SELECT pg_reload_conf();

架构设计

  • 确定集群规模:根据业务需求确定主从节点数量
  • 确定复制类型:选择同步复制或异步复制
  • 确定故障转移机制:选择手动故障转移或自动故障转移
  • 确定负载均衡方案:选择内置负载均衡或第三方负载均衡
  • 确定监控方案:选择内置监控或第三方监控

迁移步骤

1. 搭建主从复制集群

步骤1:配置主库

  1. 修改 postgresql.conf

    ini
    # 启用流复制
    wal_level = replica
    max_wal_senders = 10
    wal_keep_size = 1GB
    max_replication_slots = 10
    
    # 启用归档(可选)
    archive_mode = on
    archive_command = 'cp %p /path/to/archive/%f'
  2. 修改 pg_hba.conf

    ini
    # 允许从库连接
    host replication repl 192.168.1.0/24 md5
  3. 创建复制用户

    sql
    CREATE USER repl REPLICATION LOGIN ENCRYPTED PASSWORD 'repl_password';
  4. 重启主库

    bash
    pg_ctl -D /path/to/data restart

步骤2:配置从库

  1. 清空从库数据目录

    bash
    rm -rf /path/to/data/*
  2. 从主库同步数据

    bash
    pg_basebackup -h master_host -p 5432 -U repl -D /path/to/data -F p -X stream -P -R
  3. 修改 postgresql.conf(可选,针对从库的特定配置):

    ini
    # 从库特定配置
    hot_standby = on
    max_standby_streaming_delay = 30s
    wal_receiver_status_interval = 10s
    hot_standby_feedback = on
  4. 启动从库

    bash
    pg_ctl -D /path/to/data start
  5. 验证复制状态

    sql
    -- 在主库上检查
    SELECT * FROM pg_stat_replication;
    
    -- 在从库上检查
    SELECT pg_is_in_recovery();
    SELECT * FROM pg_stat_wal_receiver;

2. 配置负载均衡

使用 pgpool-II 配置负载均衡

  1. 安装 pgpool-II

    bash
    # CentOS/RHEL
    yum install pgpool-II-pg14
    
    # Ubuntu/Debian
    apt-get install pgpool2
  2. 配置 pgpool.conf

    ini
    # 启用负载均衡
    load_balance_mode = on
    
    # 配置主从节点
    backend_hostname0 = 'master_host'
    backend_port0 = 5432
    backend_weight0 = 1
    backend_flag0 = 'ALLOW_TO_FAILOVER'
    
    backend_hostname1 = 'slave1_host'
    backend_port1 = 5432
    backend_weight1 = 1
    backend_flag1 = 'ALLOW_TO_FAILOVER'
    
    backend_hostname2 = 'slave2_host'
    backend_port2 = 5432
    backend_weight2 = 1
    backend_flag2 = 'ALLOW_TO_FAILOVER'
    
    # 配置健康检查
    health_check_period = 5
    health_check_timeout = 20
    health_check_user = 'postgres'
    health_check_password = 'password'
  3. 配置 pcp.conf

    ini
    # 用于 PCP 管理的用户和密码
    postgres:md5password
  4. 启动 pgpool-II

    bash
    systemctl start pgpool-II
  5. 验证负载均衡

    bash
    # 连接到 pgpool-II
    psql -h pgpool_host -p 9999 -U postgres -c "SELECT current_user, inet_client_addr();"

3. 配置自动故障转移

使用 Patroni 配置自动故障转移

  1. 安装 Patroni 和 etcd

    bash
    # 安装 Patroni
    pip install patroni[etcd]
    
    # 安装 etcd(用于存储集群状态)
    yum install etcd
  2. 配置 etcd

    yaml
    # /etc/etcd/etcd.conf
    ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
    ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
    ETCD_INITIAL_ADVERTISE_PEER_URLS="http://etcd_host:2380"
    ETCD_ADVERTISE_CLIENT_URLS="http://etcd_host:2379"
    ETCD_INITIAL_CLUSTER="etcd1=http://etcd_host:2380"
    ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-1"
    ETCD_INITIAL_CLUSTER_STATE="new"
    ETCD_NAME="etcd1"
  3. 配置 Patroni

    yaml
    # patroni.yml
    scope: postgres_cluster
    namespace: /db/
    name: postgres1
    
    restapi:
      listen: postgres1:8008
      connect_address: postgres1:8008
    
    etcd:
      hosts: etcd_host:2379
    
    bootstrap:
      dcs:
        ttl: 30
        loop_wait: 10
        retry_timeout: 10
        maximum_lag_on_failover: 1048576
        postgresql:
          use_pg_rewind: true
          use_slots: true
          parameters:
            listen_addresses: "'*'"
            max_connections: 100
            wal_level: replica
            hot_standby: on
            logging_collector: on
            log_directory: 'pg_log'
            log_filename: 'postgresql-%Y-%m-%d_%H%M%S.log'
            log_truncate_on_rotation: true
            log_rotation_age: 1d
            log_rotation_size: 0
            max_wal_senders: 10
            max_replication_slots: 10
            wal_keep_size: 1GB
            archive_mode: on
            archive_command: '/bin/true'
      initdb:
      - encoding: UTF8
      - data-checksums
      pg_hba:
      - host replication replicator 127.0.0.1/32 md5
      - host replication replicator postgres0/32 md5
      - host replication replicator postgres1/32 md5
      - host replication replicator postgres2/32 md5
      - host all all 0.0.0.0/0 md5
      users:
        admin:
          password: admin_password
          options:
            - createrole
            - createdb
    
    postgresql:
      listen: postgres1:5432
      connect_address: postgres1:5432
      data_dir: /data/patroni
      bin_dir: /usr/pgsql-14/bin
      pgpass: /tmp/pgpass0
      authentication:
        replication:
          username: replicator
          password: replication_password
        superuser:
          username: postgres
          password: postgres_password
      parameters:
        unix_socket_directories: '/tmp'
    
    tags:
      nofailover: false
      noloadbalance: false
      clonefrom: false
      nosync: false
  4. 启动 Patroni

    bash
    patroni patroni.yml
  5. 验证集群状态

    bash
    # 使用 patronictl 查看集群状态
    patronictl -c patroni.yml list

4. 数据迁移

方法1:使用 pg_basebackup 进行初始同步

  1. 在主库上执行 pg_basebackup

    bash
    pg_basebackup -h master_host -p 5432 -U repl -D /path/to/data -F p -X stream -P -R
  2. 启动从库

    bash
    pg_ctl -D /path/to/data start
  3. 验证复制状态

    sql
    SELECT * FROM pg_stat_replication;

方法2:使用逻辑复制进行数据同步

  1. 在主库上创建发布

    sql
    CREATE PUBLICATION my_publication FOR ALL TABLES;
  2. 在从库上创建订阅

    sql
    CREATE SUBSCRIPTION my_subscription 
    CONNECTION 'host=master_host port=5432 dbname=dbname user=repl password=repl_password' 
    PUBLICATION my_publication;
  3. 验证复制状态

    sql
    SELECT * FROM pg_stat_subscription;

5. 应用切换

  1. 更新应用连接配置

    • 将应用连接从单机数据库切换到集群的负载均衡地址
    • 更新连接池配置
  2. 测试应用连接

    bash
    psql -h cluster_host -p 9999 -U app_user -d app_db -c "SELECT 1;"
  3. 监控应用性能

    • 监控应用响应时间
    • 监控数据库连接数
    • 监控查询性能

6. 迁移后验证

  1. 检查集群状态

    sql
    -- 检查主从状态
    SELECT * FROM pg_stat_replication;
    
    -- 检查从库状态
    SELECT pg_is_in_recovery();
  2. 检查数据一致性

    sql
    -- 在主库上执行
    CREATE TABLE test_consistency (id serial PRIMARY KEY, value text);
    INSERT INTO test_consistency (value) VALUES ('test');
    
    -- 在从库上检查
    SELECT * FROM test_consistency;
  3. 测试故障转移

    bash
    # 手动触发故障转移
    patronictl -c patroni.yml failover
    
    # 或停止主库服务模拟故障
    pg_ctl -D /path/to/master/data stop -m fast
  4. 测试负载均衡

    bash
    # 多次连接,检查是否分发到不同节点
    for i in {1..10}; do
      psql -h cluster_host -p 9999 -U postgres -c "SELECT inet_client_addr();"
    done

最佳实践

1. 集群设计最佳实践

  • 合理规划节点数量:根据业务需求和预算确定节点数量,一般建议至少3个节点(1主2从)
  • 选择合适的复制类型:对数据一致性要求高的场景选择同步复制,对性能要求高的场景选择异步复制
  • 实现自动故障转移:使用 Patroni、pgpool-II 等工具实现自动故障转移,减少人工干预
  • 配置负载均衡:使用负载均衡分发读写请求,提高系统性能和可用性
  • 实现监控和告警:配置监控系统,实时监控集群状态和性能

2. 性能优化最佳实践

  • 优化复制配置:根据业务需求调整 wal_keep_size、max_wal_senders 等参数
  • 使用连接池:使用 PgBouncer 或 pgpool-II 等连接池工具,减少数据库连接数
  • 实现读写分离:将只读请求分发到从库,提高系统整体性能
  • 优化查询:优化慢查询,减少数据库负载
  • 合理设置索引:根据查询需求合理设置索引,提高查询性能

3. 可用性优化最佳实践

  • 实现多级备份:定期进行全量备份和增量备份,确保数据安全
  • 配置 WAL 归档:启用 WAL 归档,确保可以进行 Point-in-Time Recovery (PITR)
  • 实现灾备机制:在不同地域部署从库,实现异地灾备
  • 定期进行故障演练:定期模拟故障,测试故障转移机制的有效性
  • 制定应急预案:制定详细的应急预案,确保在故障发生时能够快速响应

4. 维护最佳实践

  • 定期更新 PostgreSQL 版本:及时更新到最新的稳定版本,获取新功能和安全补丁
  • 定期更新集群软件:及时更新 Patroni、pgpool-II 等集群软件
  • 定期进行健康检查:定期检查集群节点的健康状态
  • 定期清理 WAL 日志:定期清理过期的 WAL 日志,释放磁盘空间
  • 定期更新统计信息:定期运行 ANALYZE,更新统计信息,提高查询优化器的准确性

案例分析

案例1:电商系统从单机到主从集群

背景:某电商系统使用单机 PostgreSQL 13 数据库,随着业务增长,读请求量急剧增加,需要提高系统的读性能和可用性。

解决方案

  1. 搭建 1 主 2 从的流复制集群
  2. 使用 pgpool-II 实现负载均衡和读写分离
  3. 使用 Patroni 实现自动故障转移
  4. 将应用连接指向 pgpool-II 地址

实施步骤

  1. 搭建主从复制:配置主库的流复制参数,初始化从库,验证复制状态
  2. 配置 pgpool-II:配置负载均衡和读写分离,启动 pgpool-II 服务
  3. 配置 Patroni:配置自动故障转移,启动 Patroni 服务
  4. 应用切换:将应用连接从单机数据库切换到 pgpool-II 地址
  5. 验证集群:测试负载均衡、故障转移和数据一致性

实施效果

  • 读性能提升 200%(通过 2 个从库分担读请求)
  • 可用性提高到 99.99%(通过自动故障转移)
  • 系统可以承受单个节点故障,不会影响业务

案例2:金融系统从单机到高可用集群

背景:某金融系统使用单机 PostgreSQL 12 数据库,对数据一致性和可用性要求极高,需要实现零数据丢失和自动故障转移。

解决方案

  1. 搭建 1 主 2 从的同步复制集群
  2. 使用 Patroni + etcd 实现自动故障转移
  3. 配置 WAL 归档和 PITR 恢复机制
  4. 实现异地灾备(在不同地域部署从库)

实施步骤

  1. 搭建同步复制集群:配置主库的同步复制参数,初始化从库,验证复制状态
  2. 配置 WAL 归档:启用 WAL 归档,配置归档命令
  3. 配置 Patroni:配置自动故障转移,启动 Patroni 服务
  4. 配置异地灾备:在异地部署从库,配置异步复制
  5. 应用切换:将应用连接指向集群主库地址
  6. 验证集群:测试同步复制、故障转移和数据一致性

实施效果

  • 数据零丢失(通过同步复制)
  • 可用性提高到 99.999%(通过自动故障转移和异地灾备)
  • 系统可以承受多节点故障,不会影响业务
  • 支持 PITR 恢复,可以恢复到任意时间点

总结

将 PostgreSQL 从单机迁移到集群是提高系统可用性、性能和可扩展性的重要手段。在迁移过程中,需要选择合适的集群架构,搭建主从复制,配置负载均衡和故障转移机制,进行数据迁移和应用切换,并进行全面的验证。

通过合理的集群设计、性能优化和可用性优化,可以构建高可用、高性能的 PostgreSQL 集群,满足日益增长的业务需求。同时,定期进行维护和故障演练,确保集群的长期稳定运行。

本文介绍了 PostgreSQL 从单机到集群的迁移过程、架构选择、实施步骤和最佳实践,希望能帮助 DBA 们顺利完成从单机到集群的迁移,构建高可用、高性能的 PostgreSQL 集群。