多页打印视图 点击此处打印.

返回常规视图.

部署

置备高可用PostgreSQL数据库集群!

单集群部署

在单个PostgreSQL数据库集群中的几种不同角色: primary, replica, offline, standby

  • primary:定义一个单实例的PostgreSQL集群主库,添加业务用户与数据库
  • replica:添加标准PostgreSQL物理复制从库,调整流量,并为集群添加2层VIP
  • offline:用于单独承载OLAP分析,ETL,交互式个人查询的专用离线实例,与服务接入
  • standby:定义同步复制的从库,配置法定人数提交的高一致性从库,添加额外的特殊服务
完整的三节点高可用集群
    #----------------------------------#
    # pgsql cluster: pg-test (3 nodes) #
    #----------------------------------#
    # pg-test --->  10.10.10.3 ---> 10.10.10.1{1,2,3}
    pg-test:                                # define the new 3-node cluster pg-test
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }   # primary instance, leader of cluster
        10.10.10.12: { pg_seq: 2, pg_role: replica }   # replica instance, follower of leader
        10.10.10.13: { pg_seq: 3, pg_role: replica, pg_offline_query: true } # replica with offline access
      vars:
        pg_cluster: pg-test           # define pgsql cluster name
        pg_version: 14                # test postgresql 13 with pg-test cluster
        pg_users:
          - name: test
            password: test
            pgbouncer: true
            roles: [ dbrole_admin ]
        pg_databases:                 # create a database and user named 'test'
          - name: test

        vip_mode: l2                  # enable/disable vip (require members in same LAN)
        vip_address: 10.10.10.3       # virtual ip address for this cluster
        vip_cidrmask: 8               # cidr network mask length
        vip_interface: eth1           # interface to add virtual ip

        pg_services_extra:            # extra services in addition to pg_services_default, array of service definition
          # standby service will route {ip|name}:5435 to sync replica's pgbouncer (5435->6432 standby)
          - name: standby             # required, service name, the actual svc name will be prefixed with `pg_cluster`, e.g: pg-meta-standby
            src_ip: "*"               # required, service bind ip address, `*` for all ip, `vip` for cluster `vip_address`
            src_port: 5435            # required, service exposed port (work as kubernetes service node port mode)
            dst_port: pgbouncer       # optional, destination port, postgres|pgbouncer|<port_number>   , pgbouncer(6432) by default
            check_method: http        # optional, health check method: http is the only available method for now
            check_port: patroni       # optional, health check port: patroni|pg_exporter|<port_number> , patroni(8008) by default
            check_url: /sync          # optional, health check url path, /read-only?lag=0 by default
            check_code: 200           # optional, health check expected http code, 200 by default
            selector: "[]"            # required, JMESPath to filter inventory ()
            selector_backup: "[? pg_role == `primary`]"  # primary used as backup server for standby service (will not work because /sync for )
            haproxy:                  # optional, adhoc parameters for haproxy service provider (vip_l4 is another service provider)
              maxconn: 3000           # optional, max allowed front-end connection
              balance: roundrobin     # optional, haproxy load balance algorithm (roundrobin by default, other: leastconn)
              default_server_options: 'inter 3s fastinter 1s downinter 5s rise 3 fall 3 on-marked-down shutdown-sessions slowstart 30s maxconn 3000 maxqueue 128 weight 100'

多集群部署

多个PostgreSQL集群共同提供服务。

  • 备份集群:制作现有集群的实时在线克隆,用于异地灾备。
  • 延迟集群:用于应对误删表删库等软件/人为故障,快速恢复数据。
  • Citus集群部署:部署Citus分布式数据库集群,1协调者与3数据节点
  • MatrixDB集群部署:部署Greenplum7/PostgreSQL12兼容的时序数据仓库。

1 - PGSQL Primary 主库部署

本文介绍PostgreSQL单例主库的部署方式,以及如何在主库是那个部署

单机部署

让我们以沙箱环境为例,假设您已经在沙箱元节点(10.10.10.10)使用 infra.yml 完成完整安装。

现在从最简单的案例开始,我们希望在 node-1 (10.10.10.11) 上部署一个单机数据库集群 pg-test

首先,编辑配置清单,添加pg-test集群的配置:

    pg-test:
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }
      vars:
        pg_cluster: pg-test

使用以下命令,在 10.10.10.11 节点上创建一个单主的数据库实例。

bin/createpg pg-test         # 创建 pg-test 集群
bin/createpg 10.10.10.11     # 另一种等效写法,因为集群只有该实例

添加用户

Pigsty会在数据库中默认创建四个系统用户与四类默认角色,但业务使用数据库时,应当使用专用的业务用户

在新创建的实例上添加新的业务用户,首先在集群层面定义 pg_users,添加一个新的业务用户定义。

    pg-test:
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }
      vars:
        pg_cluster: pg-test

        pg_users:                      # 定义集群层面的业务用户,数组,每个元素为一个用户定义
          - name: test                 # 用户名为 test,唯一的必选字段
            password: test             # 密码也是 test
            pgbouncer: true            # 为该用户启用 Pgbouncer 连接池
            roles: [ dbrole_admin ]    # 该用户为 管理员, 可以执行DDL

如果初始化实例/集群时,pg_users 中的用户已经定义,那么该用户将在集群初始化完毕后自动创建,否则您需要使用以下命令在 pg-test 集群中创建该业务用户

bin/createuser pg-test test  # 在 pg-test 集群上创建 test 用户

添加数据库

接下来,我们在这个实例上添加新的业务数据库,首先在集群层面定义 pg_databases,添加一个新的业务数据库定义。

    pg-test:
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }
      vars:
        pg_cluster: pg-test
        pg_users: [ { name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] } ]
        pg_databases:      # 在集群层面定义业务数据库,数组,每一个元素为一个数据库
          - name: test     # 数据库有很多可选参数,但只有一个数据库名是必选项

如果初始化实例/集群时,pg_databases 中的数据库已经定义,那么该数据库将在集群初始化完毕后自动创建,否则您需要使用以下命令在 pg-test 集群中创建该业务数据库

bin/createdb pg-test test  # 在 pg-test 集群上创建 test 数据库

创建完数据库后,您便可以使用连接串,使用该业务数据库访问该业务数据库:

psql postgres://test:test@10.10.10.11/test
pgbench -is10  postgres://test:test@10.10.10.11:5432/test

注意事项

单实例数据库无法应对硬件故障,如果该节点挂了或者磁盘损坏了,您的数据就歇菜了。

为了避免此类问题,强烈建议在生产使用时,最少为集群配置一个副本从库

2 - PGSQL Replica 从库部署

本文介绍如何为PostgreSQL集群添加只读从库副本,配置L2 VIP接入,以及流量分发。

只读从库

复制可以极大高数据库系统可靠性,是应对硬件故障的最佳手段,在生产环境中强烈建议至少使用一主一从的配置。

Pigsty原生支持设置主从复制,例如,声明典型的一主一从高可用数据库集群,可以使用:

    pg-test:
      hosts:
        
        10.10.10.11: { pg_seq: 1, pg_role: primary }
        10.10.10.12: { pg_seq: 2, pg_role: replica }  # 在集群中定义一个新从库          
      
      vars:
        pg_cluster: pg-test
        pg_users: [ { name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] } ]
        pg_databases: [ { name: test } ]

使用 bin/createpg pg-test可一次性创建出该集群来。

如果集群的主库已经存在,则可以执行 集群扩容

集群扩容

扩容现有集群分为两步:初始化新的集群实例,将流量均匀分发至新实例。

bin/createpg 10.10.10.12  # 初始化 pg-test 集群的新从库 10.10.10.12 

集群扩容后,现有流量并不会自动切换至新实例上, 需要参考 集群角色调整 的操作重新调整流量:

bin/reloadha  pg-test    # 重新配置集群流量,让新实例接入流量

集群扩容后,当主库宕机不可用时,从库会自动接管,成为新的主库。请注意,如果您的客户端使用IP地址直连数据库与连接池,则故障切换会导致连接到原来集群主库客户端应用 无法写入。 一个简单的解决方案是使用 2层VIP,绑定集群主库,通过动态绑定的VIP而非写死的主库IP,访问数据库。

2层VIP

Pigsty允许您指定一个 2层的VIP地址,以便客户端自动适应集群高可用故障切换

  1. 客户端可以始终通过此 VIP 地址访问集群主库,集群自动高可用故障切换后,客户端流量将自动适配。
  2. 您可以通过此VIP确保HAProxy负载均衡器本身的高可用。

Pigsty提供了多种不同的接入方式,L2 VIP为可选项,且只有集群所有实例位于同一个大二层网络时可用。

    pg-test:
      hosts:

        10.10.10.11: { pg_seq: 1, pg_role: primary }
        10.10.10.12: { pg_seq: 2, pg_role: replica }  # 在集群中定义一个新从库          

      vars:
        pg_cluster: pg-test
        pg_users: [ { name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] } ]
        pg_databases: [ { name: test } ]

        # 为集群定义一个 VIP : 10.10.10.3/8 网卡为 eth1 (按实际情况修改)
        vip_mode: l2                  # enable/disable vip (require members in same LAN)
        vip_address: 10.10.10.3       # virtual ip address for this cluster
        vip_cidrmask: 8               # cidr network mask length
        vip_interface: eth1           # interface to add virtual ip

如果初始化实例/集群时,PG_SERVICE 中的VIP相关配置已经定义, 那么该数据库集群将在集群初始化完毕后自动绑定该VIP,否则您需要通过以下命令手工启用VIP:

./pgsql.yml -l pg-test -t vip         # 在已有集群上加装2层VIP

启用VIP后,您可以使用VIP访问集群,也可以将一个静态域名绑定在该VIP上。

make test-ri   # 初始化 pgbench
pgbench -is10  postgres://test:test@pg-test:5436/test

make test-rw  # 添加一些 pgbench 主库写入流量
while true; do pgbench -nv -P1 -c4 --rate=64 -T10 postgres://test:test@pg-test:5433/test; done

故障切换

如果主库出现故障,或者我们希望将从库提升为新主库,可以执行 主动切换(Switchover)或 故障切换 (Failover),两者的区别主要在于集群当前的主库是否可用。

pg switchover pg-test     # 手工执行Switchover(原主库可用)
pg failover   pg-test     # 手工执行Failover (原主库不可用)

请注意,自动故障切换需要第三方进行仲裁,在Pigsty中,部署于管理节点上的DCS提供了此仲裁服务。(您也可以通过配置 dcs_servers 使用外部DCS服务)

原有主库在宕机重启后,会尝试自动重新加入集群中,并自动降级为从库追随新的主库。这一过程可能会因为某些原因失败。在这种情况下,建议将其仔细检查原因,并为集群补充添加一台新的从库,恢复一主一从的集群拓扑。

请注意,一主一从的数据库本身尽管只需要2个节点,但数据库的高可用依赖 DCS 服务,如果您没有使用 外部DCS 服务,而是重用数据库节点部署DCS服务,那么您的整个环境至少需要3个节点才足以部署生产级的基础高可用集群(3坏1)

三节点标准HA集群

如果您的整套环境只有两个节点,且没有使用外部DCS进行仲裁,则无法进行安全可靠的自动故障切换,当故障发生时,您需要手工介入,人工仲裁。 任何真正有意义的高可用方案,在没有特殊硬件(如心跳线等Fencing硬件)支持下,至少需要整个环境中有三个节点。因为高可用所依赖的仲裁者(DCS)本身的高可用至少需要三个节点。

因此,如果希望部署生产级自动高可用切换的集群,您的整个环境中应当至少有3个节点

如果您的整套环境中有三个节点,则可以使用 pigsty-dcs3.yml 中的样例, 构建一个3元节点 x 3实例PG集群的基础高可用单元。在此部署下, 三个管理节点上部署有 DCS Server (Consul/Etcd),任意一个节点故障,整个集群都可以继续正常工作。

在生产环境中,您可以使用此三节点集群作为整个集群的管控核心,管理更多的数据库集群。 在已有3节点DCS的仲裁者的情况下,您可以部署大量1主1从的基本高可用PGSQL集群,这些集群可以自动进行故障切换。

all:
  children:

  vars:
    dcs_servers: # 使用一个3节点 的高可用DCS 服务器集群,来确保元数据库本身的高可用
      meta-1: 10.10.10.10   # you could use existing dcs cluster
      meta-2: 10.10.10.11   # host which have their IP listed here will be init as server
      meta-3: 10.10.10.12   # 3 or 5 dcs nodes are recommended for production environment

您可以接入集群中任何一个实例上的Haproxy,以获取完整的集群服务。

添加更多从库

您可以为集群添加更多从库,例如为 pg-test 再扩容一台实例 pg-test-3 @ 10.10.10.13,来分担只读流量

当您有多个从库时,可以通过 pg_weight 调整各实例承载流量的相对权重(默认:100,范围:0-255)

    pg-test:
      hosts:

        10.10.10.11: { pg_seq: 1, pg_role: primary }
        10.10.10.12: { pg_seq: 2, pg_role: replica }
        10.10.10.13: { pg_seq: 3, pg_role: replica , pg_weight: 20 }

      vars:
        pg_cluster: pg-test
        pg_users: [ { name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] } ]
        pg_databases: [ { name: test } ]

        # 为集群定义一个 VIP : 10.10.10.3/8 网卡为 eth1 (按实际情况修改)
        vip_mode: l2                  # enable/disable vip (require members in same LAN)
        vip_address: 10.10.10.3       # virtual ip address for this cluster
        vip_cidrmask: 8               # cidr network mask length
        vip_interface: eth1           # interface to add virtual ip

新扩容一台从库实例

bin/createpg 10.10.10.13

调整集群负载均衡器配置:

bin/reloadha pg-test    # 应用集群负载均衡配置
make test-ro            # 生成一些只读流量,并观察流量分布

在高频访问的只读从库上运行长时间,大批量,交互式的慢查询通常并不是一个好主意,有可能会导致资源征用,出现慢查询,或复制延迟。

针对这种场景,可以为集群配置一个专用的离线从库

3 - PGSQL Offline 离线从库部署

离线从库 —— 一种不承载线上业务流量,用于ETL,只读分析查询的专用实例。

离线从库

当您的在线业务请求负载水位很大时,将数据分析/ETL/个人交互式查询,备份与数据导出等任务, 放在专用的离线只读从库上是一个更为合适的选择。

离线从库默认不承载 replica 服务,只有当所有 replica 服务中的实例均不可用时,离线实例才会用于紧急承载只读流量。

pg-test:
  hosts:
    10.10.10.11: { pg_seq: 1, pg_role: primary }
    10.10.10.12: { pg_seq: 2, pg_role: replica }
    10.10.10.13: { pg_seq: 2, pg_role: offline } # 定义一个新的Offline实例
  vars:
    pg_cluster: pg-test

使用 bin/createpg pg-test,即可创建出该集群来。 那么可以使用 bin/createpg 10.10.10.13,进行集群扩容,向集群中添加一台离线从库实例。

bin/createpg 10.10.10.13  # 向集群 pg-test 中扩容一台离线实例 10.10.10.13 
bin/reloadha pg-test      # 重新调整 pg-test 集群的负载均衡流量

离线从库的访问受到特殊限制,这是通过额外的HBA规则实现的。例如,用户应当使用Offline服务访问离线从库。

psql postgres://test:test@pg-test:5433/test

准离线从库

单独使用一台离线从库对于很多非核心场景过于奢侈,您可以将一台普通从库标记为准离线从库。

如果您只有一主一从,或者干脆只有一个主库,没有专用的离线实例,可以为该实例设置 pg_offline_query 标记。 带有该标记的实例仍然扮演原来的角色,但同时也承载 offline 服务的流量,可用作 准离线实例

pg-test:
  hosts:
    10.10.10.11: { pg_seq: 1, pg_role: primary }
    10.10.10.12: { pg_seq: 2, pg_role: replica }
    10.10.10.13: { pg_seq: 2, pg_role: replica, pg_offline_query: true } # 定义一个准离线实例
  vars:
    pg_cluster: pg-test

无论是您将一台 replica 重新标记为 offline,或者是为从库打上 pg_offline_query 的标记,您都需要使用以下命令调整集群 HBA:

bin/reloadha  pg-test   # 调整集群流量分发规则
bin/reloadhba pg-test   # 调整集群黑白名单规则

然后即可生效。

4 - PGSQL Standby 同步从库部署

同步从库 —— 与主库保持同步提交,零复制延迟的热备从库,启用额外的服务,以及法定人数同步提交。

CAP定理指出:可用性与一致性两者相互抵触,用户必须根据自己的需求进行权衡。 高可用是一方面,而另一面则是高一致,Pigsty允许您创建高一致性的集群,确保出现故障切换时数据不丢,乃至于整个集群保持实时同步一致。

正常情况下,PostgreSQL的复制延迟在几十KB/10ms的量级,对于常规业务而言可以近似忽略不计。重要的是,当主库出现故障时,尚未完成复制的数据会丢失!当您在处理非常关键与精密的业务查询时(例如和钱打交道),复制延迟可能会成为一个问题。此外,或者在主库写入后,立刻向从库查询刚才的写入(read-your-write),也会对复制延迟非常敏感。

为了解决此类问题,需要用到同步从库

同步从库

一种简单的配置同步从库的方式是使用 pg_conf = crit 模板,该模板会自动启用同步复制与校验和,适用于和钱有关的,追求一致性的场景。

    #----------------------------------#
    # pgsql cluster: pg-test (3 nodes) #
    #----------------------------------#
    # pg-test --->  10.10.10.3 ---> 10.10.10.1{1,2,3}
    pg-test:                                # define the new 3-node cluster pg-test
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }   # primary instance, leader of cluster
        10.10.10.12: { pg_seq: 2, pg_role: replica }   # replica instance, follower of leader
        10.10.10.13: { pg_seq: 3, pg_role: replica, pg_offline_query: true } # replica with offline access
      vars:
        pg_conf: crit.yml             # Crit 模板默认启用同步复制

        pg_cluster: pg-test           # define pgsql cluster name
        pg_version: 14                # test postgresql 13 with pg-test cluster
        pg_users:
          - name: test
            password: test
            pgbouncer: true
            roles: [ dbrole_admin ]
        pg_databases:                 # create a database and user named 'test'
          - name: test

        vip_mode: l2                  # enable/disable vip (require members in same LAN)
        vip_address: 10.10.10.3       # virtual ip address for this cluster
        vip_cidrmask: 8               # cidr network mask length
        vip_interface: eth1           # interface to add virtual ip

使用同步提交时,强烈建议集群至少有3个实例,否则唯一的从库故障将立即导致主库不可用。

启用同步

对于已有的数据库集群,您可以在集群创建完毕后,在元节点上执行 pg edit-config <cluster.name> , 编辑集群配置文件,修改参数synchronous_mode的值为true并应用即可。

$ pg edit-config pg-test
---
+++
-synchronous_mode: false
+synchronous_mode: true
 synchronous_mode_strict: false

Apply these changes? [y/N]: y

使用 pg list pg-test 打印集群状态,如果出现了一个 Sync Standby,则说明已经成功为集群配置了同步从库。 在出现故障时,该同步从库将作为第一顺位候选继承人接管,确保没有数据损失。

$ pg list pg-test
+ Cluster: pg-test (7105646627234977503) +---------+----+-----------+-----------------+
| Member    | Host        | Role         | State   | TL | Lag in MB | Tags            |
+-----------+-------------+--------------+---------+----+-----------+-----------------+
| pg-test-1 | 10.10.10.11 | Leader       | running |  3 |           | clonefrom: true |
| pg-test-2 | 10.10.10.12 | Replica      | running |  3 |         0 | clonefrom: true |
| pg-test-3 | 10.10.10.13 | Sync Standby | running |  3 |         0 | clonefrom: true |
+-----------+-------------+--------------+---------+----+-----------+-----------------+

在PG中启用同步提交,默认会有一个从库实例被选为同步从库,而其他的实例会仍然会使用异步提交模式,以降低事务延迟,提高性能。如果您需要在整个集群范围内获得更强的一致性,可以使用法定人数同步提交

法定人数同步提交

在默认情况下,同步复制会从所有候选从库 挑选一个实例,作为同步从库,任何主库事务只有当复制到从库并Flush至磁盘上时,方视作成功提交并返回。 如果我们期望更高的数据持久化保证,例如,在一个一主三从的四实例集群中,至少有两个从库成功刷盘后才确认提交,则可以使用法定人数提交。

使用法定人数提交时,需要修改 PostgreSQL 中 synchronous_standby_names 参数的值,并配套修改Patroni中 synchronous_node_count 的值。假设三个从库分别为 pg-test-2, pg-test-3, pg-test-4 ,那么应当配置:

  • synchronous_standby_names = ANY 2 (pg-test-2, pg-test-3, pg-test-4)
  • synchronous_node_count : 2
pg-test:
  hosts:
    10.10.10.10: { pg_seq: 1, pg_role: primary } # pg-test-1
    10.10.10.11: { pg_seq: 2, pg_role: replica } # pg-test-2
    10.10.10.12: { pg_seq: 3, pg_role: replica } # pg-test-3
    10.10.10.13: { pg_seq: 4, pg_role: replica } # pg-test-4
  vars:
    pg_cluster: pg-test

执行pg edit-config pg-test,并修改配置如下:

$ pg edit-config pg-test
---
+++
@@ -82,10 +82,12 @@
     work_mem: 4MB
+    synchronous_standby_names: 'ANY 2 (pg-test-2, pg-test-3, pg-test-4)'
 
-synchronous_mode: false
+synchronous_mode: true
+synchronous_node_count: 2
 synchronous_mode_strict: false

Apply these changes? [y/N]: y

应用后,即可看到配置生效,出现两个Sync Standby,当集群出现Failover或扩缩容时,请相应调整这些参数以免服务不可用。

+ Cluster: pg-test (7080814403632534854) +---------+----+-----------+-----------------+
| Member    | Host        | Role         | State   | TL | Lag in MB | Tags            |
+-----------+-------------+--------------+---------+----+-----------+-----------------+
| pg-test-1 | 10.10.10.10 | Leader       | running |  1 |           | clonefrom: true |
| pg-test-2 | 10.10.10.11 | Sync Standby | running |  1 |         0 | clonefrom: true |
| pg-test-3 | 10.10.10.12 | Sync Standby | running |  1 |         0 | clonefrom: true |
| pg-test-4 | 10.10.10.13 | Replica      | running |  1 |         0 | clonefrom: true |
+-----------+-------------+--------------+---------+----+-----------+-----------------+

接入同步服务

对于启用同步提交的集群,您可以在参考配置文件,在集群中额外配置 standby 服务,提供与主库完全一致的无延迟读取服务。

    #----------------------------------#
    # pgsql cluster: pg-test (3 nodes) #
    #----------------------------------#
    # pg-test --->  10.10.10.3 ---> 10.10.10.1{1,2,3}
    pg-test:                                # define the new 3-node cluster pg-test
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }   # primary instance, leader of cluster
        10.10.10.12: { pg_seq: 2, pg_role: replica }   # replica instance, follower of leader
        10.10.10.13: { pg_seq: 3, pg_role: replica, pg_offline_query: true } # replica with offline access
      vars:
        pg_cluster: pg-test           # define pgsql cluster name
        pg_version: 14                # test postgresql 13 with pg-test cluster
        pg_users:
          - name: test
            password: test
            pgbouncer: true
            roles: [ dbrole_admin ]
        pg_databases:                 # create a database and user named 'test'
          - name: test

        vip_mode: l2                  # enable/disable vip (require members in same LAN)
        vip_address: 10.10.10.3       # virtual ip address for this cluster
        vip_cidrmask: 8               # cidr network mask length
        vip_interface: eth1           # interface to add virtual ip

        pg_services_extra:            # extra services in addition to pg_services_default, array of service definition
          # standby service will route {ip|name}:5435 to sync replica's pgbouncer (5435->6432 standby)
          - name: standby             # required, service name, the actual svc name will be prefixed with `pg_cluster`, e.g: pg-meta-standby
            src_ip: "*"               # required, service bind ip address, `*` for all ip, `vip` for cluster `vip_address`
            src_port: 5435            # required, service exposed port (work as kubernetes service node port mode)
            dst_port: pgbouncer       # optional, destination port, postgres|pgbouncer|<port_number>   , pgbouncer(6432) by default
            check_method: http        # optional, health check method: http is the only available method for now
            check_port: patroni       # optional, health check port: patroni|pg_exporter|<port_number> , patroni(8008) by default
            check_url: /sync          # optional, health check url path, /read-only?lag=0 by default
            check_code: 200           # optional, health check expected http code, 200 by default
            selector: "[]"            # required, JMESPath to filter inventory ()
            selector_backup: "[? pg_role == `primary`]"  # primary used as backup server for standby service (will not work because /sync for )
            haproxy:                  # optional, adhoc parameters for haproxy service provider (vip_l4 is another service provider)
              maxconn: 3000           # optional, max allowed front-end connection
              balance: roundrobin     # optional, haproxy load balance algorithm (roundrobin by default, other: leastconn)
              default_server_options: 'inter 3s fastinter 1s downinter 5s rise 3 fall 3 on-marked-down shutdown-sessions slowstart 30s maxconn 3000 maxqueue 128 weight 100'

新定义的服务,需要重新配置负载均衡器方可生效。 您可以检查HAProxy负载均衡器流量管控页面,看看哪些实例正在承载同步服务。

bin/reloadha pg-test # 将新定义的standby服务加载到LB中

5 - PGSQL Standby Cluster 同步集群

同步集群 —— 整个集群作为另一个集群的物理备份集群,可用于灾备Switchover

备份集群

您可以使用 Standby Cluster 的方式,制作现有集群的克隆,使用这种方式,您可以从现有数据库平滑迁移至Pigsty集群中。

创建 Standby Cluster 的方式无比简单,您只需要确保备份集群的主库上配置有合适的 pg_upstream 参数,即可自动从原始上游拉取备份。

    # pg-test是原始数据库
    pg-test:
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }
      vars:
        pg_cluster: pg-test
        pg_version: 14
        pg_users: [ { name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] } ]
        pg_databases: [ { name: test } ]
    
    # pg-test2将作为pg-test1的Standby Cluster
    pg-test2:
      hosts:
        10.10.10.12: { pg_seq: 1, pg_role: primary , pg_upstream: 10.10.10.11 } # 实际角色为 Standby Leader
        10.10.10.13: { pg_seq: 2, pg_role: replica }
      vars:
        pg_cluster: pg-test2
        pg_version: 14          # 制作Standby Cluster时,数据库大版本必须保持一致!
bin/createpg pg-test     # 创建原始集群
bin/createpg pg-test2    # 创建备份集群

提升备份集群

当您想要将整个备份集群提升为一个独立运作的集群时,编辑新集群的Patroni配置文件,移除所有standby_cluster配置,备份集群中的Standby Leader会被提升为独立的主库。

pg edit-config pg-test2  # 移除 standby_cluster 配置定义并应用

移除下列配置:整个standby_cluster定义部分。

-standby_cluster:
-  create_replica_methods:
-  - basebackup
-  host: 10.10.10.11
-  port: 5432

修改备份集群上游复制源

当源集群发生Failover主库发生变化时,您需要调整备份集群的复制源。执行pg edit-config <cluster>,并修改standby_cluster中的源地址为新主库,应用即可生效。这里需要注意,从源集群的从库进行复制是可行的,源集群发生Failover并不会影响备份集群的复制。但新集群在只读从库上无法创建复制槽,可能出现相关报错,并存在潜在的复制中断风险,建议及时调整备份集群的上游复制源。

 standby_cluster:
   create_replica_methods:
   - basebackup
-  host: 10.10.10.13
+  host: 10.10.10.12
   port: 5432

修改 standby_cluster.host 中复制上游的IP地址,应用即可生效(无需重启,Reload即可)。


级连复制

题外话,如果您在集群内指定不同于主库的另一个从库IP作为pg_upstream,那么该从库将尝试从指定从库进行复制,而不是直接从主库进行复制。

在创建集群时,如果为集群中的某个从库指定 pg_upstream 参数(指定为集群中另一个从库),那么该实例将尝试从该指定从库构建逻辑复制。

在这种情况下,配置该参数的实例将成为一个 级联从库 , 而其复制源实例 则被称为 桥接实例

pg-test:
  hosts:
    10.10.10.11: { pg_seq: 1, pg_role: primary }
    10.10.10.12: { pg_seq: 2, pg_role: replica }  # 桥接从库,从主库接收WAL变更并转发至3号实例
    10.10.10.13: { pg_seq: 2, pg_role: replica, pg_upstream: 10.10.10.12 } # 尝试从2号从库而非主库复制,级联从库。
  vars:
    pg_cluster: pg-test

通常情况下不建议使用级联从库,级联从库通常用于从库非常多的情况,例如1主30从的情况下,主库要同时发送30份WAL。 另一种更好的办法是,您可以在主库上挂1~3个直接的从库,然后挑选1~2个作为桥接从库,把其他20多个从库挂在桥接库上,让桥接库承担分发WAL的网络流量与负载。


延迟集群

如果您为备份集群配置一个显式的复制延迟,就可以制作 延迟集群

高可用与主从复制可以解决机器硬件故障带来的问题,但无法解决软件Bug与人为操作导致的故障,例如:误删库删表。误删数据通常需要用到冷备份,但另一种更优雅高效快速的方式是事先准备一个延迟从库。

6 - PGSQL Delayed Cluster 延时集群

本文介绍PostgreSQL延时从库集群 —— 专门用于应对删库删表问题的特殊从库

高可用与主从复制可以解决机器硬件故障带来的问题,但无法解决软件Bug与人为操作导致的故障,例如:误删库删表。误删数据通常需要用到冷备份,但另一种更优雅高效快速的方式是事先准备一个延迟从库。

您可以使用 备份集群 的功能创建延时从库,例如,现在您希望为pg-test 集群指定一个延时从库:pg-testdelay,该集群是pg-test1小时前的状态。因此如果出现了误删数据,您可以立即从延时从库中获取并回灌入原始集群中。

    # pg-test是原始数据库
    pg-test:
      hosts:
        10.10.10.11: { pg_seq: 1, pg_role: primary }
      vars:
        pg_cluster: pg-test
        pg_version: 14
        pg_users: [ { name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] } ]
        pg_databases: [ { name: test } ]

    # pg-test2将作为pg-test1的Standby Cluster
    pg-test2:
      hosts:
        10.10.10.12: { pg_seq: 1, pg_role: primary , pg_upstream: 10.10.10.11 , pg_delay: 10min }
        10.10.10.13: { pg_seq: 2, pg_role: replica }
      vars:
        pg_cluster: pg-test2
        pg_version: 14          # 制作Standby Cluster时,数据库大版本必须保持一致!

创建完毕后,在元节点使用 pg edit-config pg-testdelay编辑延时集群的Patroni配置文件,修改 standby_cluster.recovery_min_apply_delay 为你期待的值,例如1h,应用即可。(注意分钟的单位是min不是m

 standby_cluster:
   create_replica_methods:
   - basebackup
   host: 10.10.10.11
   port: 5432
+  recovery_min_apply_delay: 1h

注意事项

您需要确保整个原始集群始终有微量的写入(例如用一张心跳表来实现),否则如果当整个集群处于完全没有写入的静止状态时,当主库上执行DROP TABLETRUNCATE TABLE操作时,从库上对应的表也会锁住无法查询。

7 - Citus集群部署

Pigsty可用于部署加装分布式数据库扩展插件Citus的分布式PostgreSQL集群

Citus是一个PostgreSQL生态的分布式扩展插件,默认情况下Pigsty安装Citus,但不启用。

pigsty-citus.yml 提供了一个部署Citus集群的配置文件案例。为了启用Citus,您需要修改以下参数:

  • max_prepared_transaction: 修改为一个大于max_connections的值,例如800。
  • pg_libs:必须包含citus,并放置在最前的位置。
  • 您需要在业务数据库中包含 citus 扩展插件(但您也可以事后手工通过CREATE EXTENSION自行安装)

配置样例

#----------------------------------#
# cluster: citus coordinator
#----------------------------------#
pg-meta:
  hosts:
    10.10.10.10: { pg_seq: 1, pg_role: primary , pg_offline_query: true }
  vars:
    pg_cluster: pg-meta
    vip_address: 10.10.10.2
    pg_users: [ { name: citus , password: citus , pgbouncer: true , roles: [ dbrole_admin ] } ]
    pg_databases: [ { name: meta , owner: citus , extensions: [ { name: citus } ] } ]

#----------------------------------#
# cluster: citus data nodes
#----------------------------------#
pg-node1:
  hosts:
    10.10.10.11: { pg_seq: 1, pg_role: primary }
  vars:
    pg_cluster: pg-node1
    vip_address: 10.10.10.3
    pg_users: [ { name: citus , password: citus , pgbouncer: true , roles: [ dbrole_admin ] } ]
    pg_databases: [ { name: meta , owner: citus , extensions: [ { name: citus } ] } ]

pg-node2:
  hosts:
    10.10.10.12: { pg_seq: 1, pg_role: primary  , pg_offline_query: true }
  vars:
    pg_cluster: pg-node2
    vip_address: 10.10.10.4
    pg_users: [ { name: citus , password: citus , pgbouncer: true , roles: [ dbrole_admin ] } ]
    pg_databases: [ { name: meta , owner: citus , extensions: [ { name: citus } ] } ]

pg-node3:
  hosts:
    10.10.10.13: { pg_seq: 1, pg_role: primary  , pg_offline_query: true }
  vars:
    pg_cluster: pg-node3
    vip_address: 10.10.10.5
    pg_users: [ { name: citus , password: citus , pgbouncer: true , roles: [ dbrole_admin ] } ]
    pg_databases: [ { name: meta , owner: citus , extensions: [ { name: citus } ] } ]

注册数据节点

接下来,您需要参照Citus多节点部署指南,在 Coordinator 节点上,执行以下命令以添加数据节点:

sudo su - postgres; psql meta 
SELECT * from citus_add_node('10.10.10.11', 5432);
SELECT * from citus_add_node('10.10.10.12', 5432);
SELECT * from citus_add_node('10.10.10.13', 5432);
SELECT * FROM citus_get_active_worker_nodes();
  node_name  | node_port
-------------+-----------
 10.10.10.11 |      5432
 10.10.10.13 |      5432
 10.10.10.12 |      5432
(3 rows)

成功添加数据节点后,您可以使用以下命令,在协调者上创建样例数据表,并将其分布到每个数据节点上。

-- 声明一个分布式表
CREATE TABLE github_events
(
    event_id     bigint,
    event_type   text,
    event_public boolean,
    repo_id      bigint,
    payload      jsonb,
    repo         jsonb,
    actor        jsonb,
    org          jsonb,
    created_at   timestamp
) PARTITION BY RANGE (created_at);
-- 创建分布式表
SELECT create_distributed_table('github_events', 'repo_id');

更多Citus相关功能介绍,请参考Citus官方文档

8 - MatrixDB部署

Pigsty可用于部署与监控MatrixDB(等于Greenplum 7+时序数据库)

因为目前MatrixDB使用的是PostgreSQL 12的内核,而原生Greenplum仍然使用9.6内核,因此优先使用MatrixDB替代Greenplum实现,后续将添加原生的Greenplum支持。

实体概念模型

MatrixDB在逻辑上由两部分组成,Master与Segments,两者均由PostgreSQL实例组成,实例分为四类:Master/Standby/Primary/Mirror

  • Master为用户直接接触的访问端点,用于承接查询,一套MatrixDB部署仅有一个,通常使用独立节点部署。
  • Standby是Master实例的物理从库,用于当Master故障时顶替,是可选的组件,通常也使用独立节点部署。
  • 一套MatrixDB部署通常有多个Segment,每个Segment通常由一个必选的 primary 实例与一个 可选的 mirror 实例组成。
  • Segment的primary负责实际存储与计算,mirror通常不承担读写流量,当primary宕机时顶替primary,通常与primary分布在不同节点上。
  • Segment的primary与mirror分布由MatrixDB安装向导决定,在集群的Segments节点上通常可能存在有多个不同的Segment实例

部署惯例

  • Master集群 (master/standby) (gp_role = master) 构成一个PostgreSQL集群,通常命名包含mdw,如mx-mdw
  • 每个Segment (primary/mirror) (gp_role = segment) 构成一个PostgreSQL集群,通常集群命名包含seg,如 mx-seg1, mx-seg2
  • 用户应当显式为集群节点命名,例如 mx-sdw-1, mx-sdw-2, …

下载软件

MatrixDB & Greenplum 的RPM包并不是标准Pigsty部署的一部分,因此不会放入默认的pkg.tgz中。 MatrixDB & Greenplum 的RPM包及其完整依赖将打包为一个单独的离线软件包 matrix.tgz。 您可以向Pigsty元节点上添加新的matrix源。

# 下载地址(Github):https://github.com/Vonng/pigsty/releases/download/v1.5.1/matrix.tgz
# 下载地址(China CDN):http://download.pigsty.cc/v1.5.1/matrix.tgz
# 下载脚本,在元节点上,pigsty目录下,直接使用 download matrix 下载并解压
./download matrix

该命令会创建一个 /www/matrix.repo 文件,默认情况下,您可以访问http://pigsty/matrix.repo获取该Repo,该Repo文件指向 http://pigsty/matrix目录。

配置

MatrixDB / Greenplum 的安装将复用 PGSQL 任务与配置,专属配置参数为 gp_rolepg_instances

配置文件pigsty-mxdb.yml 给出了一个在四节点沙箱环境部署MatrixDB的样例。

使用 `configure -m mxdb`,将自动使用该配置文件作为配置模板。
./configure -m mxdb

此配置文件中 node_repo_local_urls添加了新Yum源地址,http://pigsty/matrix.repo 确保所有节点都可以访问Matrix Repo。

开始部署

在四节点沙箱环境中部署MatrixDB,注意,默认将使用DBSU mxadmin:mxadmin 作为监控用户名与密码

# 如果您准备在meta节点上部署 MatrixDB Master,添加no_cmdb选项,否则正常安装即可。
./infra.yml -e no_cmdb=true   

# 配置所有用于安装MatrixDB的节点
./nodes.yml

# 在上述节点上安装MatrixDB
./pigsty-matrixdb.yml

安装完成后,您需要通过MatrixDB 提供的WEB UI完成接下来的安装。打开 http://mx.pigsty 或访问 http://10.10.10.10:8240 ,填入 pigsty-matrixdb.yml 最后输出的初始用户密码进入安装向导。

按照提示依次添加MatrixDB的节点:10.10.10.11, 10.10.10.12, 10.10.10.13,点击确认安装并等待完成后,进行下一步。

因为监控默认使用 mxadmin:mxadmin 作为监控用户名密码,请填入mxadmin 或您自己的密码。

如果您在安装向导中指定了不同的密码, 请一并更改 pg_monitor_usernamepg_monitor_password 变量(如果您使用不同于dbsu的用户,通常还需要在所有实例上配置额外的HBA)。

请注意,目前MatrixDB / Greenplum 在节点上分配 Segment的逻辑并不确定。当初始化完成后,您可以修改 pg_instances 中Segment实例的定义,并重新部署监控以反映真实拓扑。

收尾工作

最后,在Greenplum/MatrixDB Master节点上手工执行以下命令,允许监控组件访问从库,并重启生效。

sudo su - mxadmin
psql postgres -c "ALTER SYSTEM SET hot_standby = on;"  # 配置 hot_standby=on 以允许从库查询
gpconfig -c hot_standby -v on -m on                    # 配置 hot_standby=on 以允许从库查询
gpstop -a -r -M immediate                              # 立即重启MatrixDB以生效

然后,您便可以从监控系统中,观察到所有MatrixDB集群。MatrixDB Dashboard 提供了关于数据仓库的整体监控概览。

可选项目

您可以将 MatrixDB 的 Master集群视作一个普通 PostgreSQL 集群,使用 pgsql-createdbpgsql-createuser 创建业务数据库与用户。

bin/createuser mx-mdw  dbuser_monitor   # 在Master主库上创建监控用户
bin/createdb   mx-mdw  matrixmgr        # 在Master主库上创建监控专用数据库
bin/createdb   mx-mdw  meta             # 在Master主库上创建新数据库

9 - PGSQL目录结构

PostgreSQL所使用的目录层次结构

Postgres目录结构

以下参数与PostgreSQL数据库目录相关

  • pg_dbsu_home:Postgres默认用户的家目录,默认为/var/lib/pgsql
  • pg_bin_dir:Postgres二进制目录,默认为/usr/pgsql/bin/
  • pg_data:Postgres数据库目录,默认为/pg/data
  • pg_fs_main:Postgres主数据盘挂载点,默认为/export
  • pg_fs_bkup:Postgres备份盘挂载点,默认为/var/backups(可选,也可以选择备份到主数据盘上的子目录)
#------------------------------------------------------------------------------
# Create Directory
#------------------------------------------------------------------------------
# this assumes that
#   /pg is shortcut for postgres home
#   {{ pg_fs_main }} contains the main data             (MUST ALREADY MOUNTED)
#   {{ pg_fs_bkup }} contains archive and backup data   (MUST ALREADY MOUNTED)
#   cluster-version is the default parent folder for pgdata (e.g pg-test-12)
#------------------------------------------------------------------------------
# default variable:
#     pg_fs_main = /export           fast ssd
#     pg_fs_bkup = /var/backups      cheap hdd
#
#     /pg      -> /export/postgres/pg-test-12
#     /pg/data -> /export/postgres/pg-test-12/data
#------------------------------------------------------------------------------
- name: Create postgresql directories
  tags: pg_dir
  become: yes
  block:
    - name: Make sure main and backup dir exists
      file: path={{ item }} state=directory owner=root mode=0777
      with_items:
        - "{{ pg_fs_main }}"
        - "{{ pg_fs_bkup }}"

    # pg_cluster_dir:    "{{ pg_fs_main }}/postgres/{{ pg_cluster }}-{{ pg_version }}"
    - name: Create postgres directory structure
      file: path={{ item }} state=directory owner={{ pg_dbsu }} group=postgres mode=0700
      with_items:
        - "{{ pg_fs_main }}/postgres"
        - "{{ pg_cluster_dir }}"
        - "{{ pg_cluster_dir }}/bin"
        - "{{ pg_cluster_dir }}/log"
        - "{{ pg_cluster_dir }}/tmp"
        - "{{ pg_cluster_dir }}/conf"
        - "{{ pg_cluster_dir }}/data"
        - "{{ pg_cluster_dir }}/meta"
        - "{{ pg_cluster_dir }}/stat"
        - "{{ pg_cluster_dir }}/change"
        - "{{ pg_backup_dir }}/postgres"
        - "{{ pg_backup_dir }}/arcwal"
        - "{{ pg_backup_dir }}/backup"
        - "{{ pg_backup_dir }}/remote"

PG二进制目录结构

在RedHat/CentOS上,默认的Postgres发行版安装位置为

/usr/pgsql-${pg_version}/

安装剧本会自动创建指向当前安装版本的软连接,例如,如果安装了14版本的Postgres,则有:

/usr/pgsql -> /usr/pgsql-14

因此,默认的pg_bin_dir/usr/pgsql/bin/,该路径会在/etc/profile.d/pgsql.sh中添加至所有用户的PATH环境变量中。

PG数据目录结构

Pigsty假设用于部署数据库实例的单个节点上至少有一块主数据盘(pg_fs_main),以及一块可选的备份数据盘(pg_fs_bkup)。通常主数据盘是高性能SSD,而备份盘是大容量廉价HDD。

#------------------------------------------------------------------------------
# Create Directory
#------------------------------------------------------------------------------
# this assumes that
#   /pg is shortcut for postgres home
#   {{ pg_fs_main }} contains the main data             (MUST ALREADY MOUNTED)
#   {{ pg_fs_bkup }} contains archive and backup data   (MAYBE ALREADY MOUNTED)
#   {{ pg_cluster }}-{{ pg_version }} is the default parent folder 
#    for pgdata (e.g pg-test-14)
#------------------------------------------------------------------------------
# default variable:
#     pg_fs_main = /export           fast ssd
#     pg_fs_bkup = /var/backups      cheap hdd
#
#     /pg      -> /export/postgres/pg-test-14
#     /pg/data -> /export/postgres/pg-test-14/data

PG数据库集簇目录结构

# basic
{{ pg_fs_main }}     /data                      # contains all business data (pg,consul,etc..)
{{ pg_dir_main }}    /data/postgres             # contains postgres main data
{{ pg_cluster_dir }} /data/postgres/pg-test-14  # contains cluster `pg-test` data (of version 13)
                     /data/postgres/pg-test-14/bin            # binary scripts
                     /data/postgres/pg-test-14/log            # misc logs
                     /data/postgres/pg-test-14/tmp            # tmp, sql files, records
                     /data/postgres/pg-test-14/conf           # configurations
                     /data/postgres/pg-test-14/data           # main data directory
                     /data/postgres/pg-test-14/meta           # identity information
                     /data/postgres/pg-test-14/stat           # stats information
                     /data/postgres/pg-test-14/change         # changing records

{{ pg_fs_bkup }}     /var/backups                      # contains all backup data (pg,consul,etc..)
{{ pg_dir_bkup }}    /var/backups/postgres             # contains postgres backup data
{{ pg_backup_dir }}  /var/backups/postgres/pg-test-14  # contains cluster `pg-test` backup (of version 13)
                     /var/backups/postgres/pg-test-14/backup   # base backup
                     /var/backups/postgres/pg-test-14/arcwal   # WAL archive
                     /var/backups/postgres/pg-test-14/remote   # mount NFS/S3 remote resources here

# links
/pg             -> /data/postgres/pg-test-14                 # pg root link
/pg/data        -> /data/postgres/pg-test-14/data            # real data dir
/pg/backup      -> /var/backups/postgres/pg-test-14/backup   # base backup
/pg/arcwal      -> /var/backups/postgres/pg-test-14/arcwal   # WAL archive
/pg/remote      -> /var/backups/postgres/pg-test-14/remote   # mount NFS/S3 remote resources here

Pgbouncer配置文件结构

Pgbouncer使用Postgres用户运行,配置文件位于/etc/pgbouncer。配置文件包括:

  • pgbouncer.ini,主配置文件
  • userlist.txt:列出连接池中的用户
  • pgb_hba.conf:列出连接池用户的访问权限
  • database.txt:列出连接池中的数据库