如何使用 Pigsty 维护现有的 PostgreSQL 集群?
本章节提供 PostgreSQL 常见管理任务的标准操作流程(SOP):
在 Pigsty 中,数据库管理采用 IaC 的风格,先在配置清单中定义,然后执行剧本执行。
在没有定义 baseline SQL 的情况下,执行 pgsql-db.yml 剧本是幂等的。
它会将指定集群中的指定数据库调整至配置清单中的目标状态。
请注意,部分参数仅能在创建时指定。修改这些参数需要先删除再创建数据库(使用 state: recreate 重建数据库)。
业务数据库定义在数据库集群参数 pg_databases 中,这是一个数据库定义构成的对象数组。
数组内的数据库按照定义顺序依次创建,因此后面定义的数据库可以使用先前定义的数据库作为模板。
下面是 Pigsty 演示环境中默认集群 pg-meta 中的数据库定义:
pg-meta:
hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
vars:
pg_cluster: pg-meta
pg_databases:
- { name: meta ,baseline: cmdb.sql ,comment: pigsty meta database ,schemas: [pigsty] ,extensions: [{name: postgis, schema: public}, {name: timescaledb}]}
- { name: grafana ,owner: dbuser_grafana ,revokeconn: true ,comment: grafana primary database }
- { name: bytebase ,owner: dbuser_bytebase ,revokeconn: true ,comment: bytebase primary database }
- { name: kong ,owner: dbuser_kong ,revokeconn: true ,comment: kong the api gateway database }
- { name: gitea ,owner: dbuser_gitea ,revokeconn: true ,comment: gitea meta database }
- { name: wiki ,owner: dbuser_wiki ,revokeconn: true ,comment: wiki meta database }
- { name: noco ,owner: dbuser_noco ,revokeconn: true ,comment: nocodb database }
唯一必选的字段是 name,它应该是当前 PostgreSQL 集群中有效且唯一的数据库名称,其他参数都有合理的默认值。
关于数据库定义参数的完整参考,请查阅 数据库配置参考。
要在现有的 PostgreSQL 集群上创建新的业务数据库,请将数据库定义添加到 all.children.<cls>.pg_databases,然后执行:
bin/pgsql-db <cls> <dbname> # 等效于:pgsql-db.yml -l <cls> -e dbname=<dbname>
示例:创建名为 myapp 的业务数据库
pg-meta:
vars:
pg_databases:
- name: myapp
owner: dbuser_myapp
schemas: [app]
extensions:
- { name: pg_trgm }
- { name: btree_gin }
comment: my application database
bin/pgsql-db pg-meta myapp
执行效果:
myappdbuser_myappapppg_trgm 和 btree_gin不建议手工使用 SQL 创建业务数据库,特别是需要使用 Pgbouncer 连接池时。
使用 bin/pgsql-db 工具创建数据库会自动处理连接池配置和监控注册。
修改数据库属性可以通过更新配置并重新执行剧本来完成:
bin/pgsql-db <cls> <dbname> # 幂等操作,可重复执行
| 属性 | 说明 | 示例 |
|---|---|---|
owner | 数据库属主 | owner: dbuser_new |
tablespace | 默认表空间(会触发数据迁移) | tablespace: fast_ssd |
is_template | 是否标记为模板数据库 | is_template: true |
allowconn | 是否允许连接 | allowconn: false |
connlimit | 连接数限制 | connlimit: 100 |
revokeconn | 是否回收 PUBLIC 的 CONNECT 权限 | revokeconn: true |
comment | 备注信息 | comment: 新的备注 |
parameters | 数据库级参数 | 见下方示例 |
schemas | 添加/删除模式(增量操作) | 见管理模式 |
extensions | 添加/删除扩展(增量操作) | 见管理扩展 |
pgbouncer | 是否加入连接池 | pgbouncer: false |
pool_* | 连接池参数 | 见连接池配置 |
以下属性在数据库创建后无法修改,需要使用 state: recreate 重建数据库:
template - 模板数据库encoding - 字符编码locale / lc_collate / lc_ctype - 本地化设置locale_provider / icu_locale / icu_rules / builtin_locale - 本地化提供者设置strategy - 克隆策略- name: myapp
owner: dbuser_new_owner # 修改为新属主
bin/pgsql-db pg-meta myapp
执行的 SQL:
ALTER DATABASE "myapp" OWNER TO "dbuser_new_owner";
GRANT ALL PRIVILEGES ON DATABASE "myapp" TO "dbuser_new_owner";
- name: myapp
connlimit: 100 # 限制最大 100 个连接
执行的 SQL:
ALTER DATABASE "myapp" CONNECTION LIMIT 100;
- name: myapp
owner: dbuser_myapp
revokeconn: true # 回收 PUBLIC 的 CONNECT 权限
执行的 SQL:
REVOKE CONNECT ON DATABASE "myapp" FROM PUBLIC;
GRANT CONNECT ON DATABASE "myapp" TO "replicator";
GRANT CONNECT ON DATABASE "myapp" TO "dbuser_monitor";
GRANT CONNECT ON DATABASE "myapp" TO "dbuser_dba" WITH GRANT OPTION;
GRANT CONNECT ON DATABASE "myapp" TO "dbuser_myapp" WITH GRANT OPTION;
要恢复公共连接权限,设置 revokeconn: false:
- name: myapp
revokeconn: false # 恢复 PUBLIC 的 CONNECT 权限
执行的 SQL:
GRANT CONNECT ON DATABASE "myapp" TO PUBLIC;
- name: app_template
is_template: true # 允许任何有 CREATEDB 权限的用户克隆
执行的 SQL:
ALTER DATABASE "app_template" IS_TEMPLATE true;
数据库级参数通过 parameters 字典配置,会生成 ALTER DATABASE ... SET 语句。
- name: myapp
parameters:
work_mem: '256MB'
maintenance_work_mem: '512MB'
statement_timeout: '30s'
search_path: 'app,public'
执行的 SQL:
ALTER DATABASE "myapp" SET "work_mem" = '256MB';
ALTER DATABASE "myapp" SET "maintenance_work_mem" = '512MB';
ALTER DATABASE "myapp" SET "statement_timeout" = '30s';
ALTER DATABASE "myapp" SET "search_path" = 'app,public';
使用特殊值 DEFAULT(大小写不敏感)可以将参数重置为 PostgreSQL 默认值:
- name: myapp
parameters:
work_mem: DEFAULT # 重置为 PostgreSQL 默认值
statement_timeout: DEFAULT
执行的 SQL:
ALTER DATABASE "myapp" SET "work_mem" = DEFAULT;
ALTER DATABASE "myapp" SET "statement_timeout" = DEFAULT;
| 参数 | 说明 | 示例值 |
|---|---|---|
work_mem | 查询工作内存 | '64MB' |
maintenance_work_mem | 维护操作内存 | '256MB' |
statement_timeout | 语句超时时间 | '30s' |
lock_timeout | 锁等待超时 | '10s' |
idle_in_transaction_session_timeout | 空闲事务超时 | '10min' |
search_path | Schema 搜索路径 | 'app,public' |
default_tablespace | 默认表空间 | 'fast_ssd' |
temp_tablespaces | 临时表空间 | 'temp_ssd' |
log_statement | 日志记录级别 | 'ddl' |
模式(Schema)通过 schemas 数组配置,支持创建、指定属主和删除操作。
- name: myapp
schemas:
# 简单形式:只指定模式名
- app
- api
# 完整形式:指定属主
- { name: core, owner: dbuser_myapp }
执行的 SQL:
CREATE SCHEMA IF NOT EXISTS "app";
CREATE SCHEMA IF NOT EXISTS "api";
CREATE SCHEMA IF NOT EXISTS "core" AUTHORIZATION "dbuser_myapp";
使用 owner 字段可以为模式指定属主,这在多租户或权限隔离场景中非常有用:
- name: multi_tenant_db
owner: dbuser_admin
schemas:
- { name: tenant_a, owner: dbuser_tenant_a }
- { name: tenant_b, owner: dbuser_tenant_b }
- { name: shared, owner: dbuser_admin }
使用 state: absent 标记要删除的模式:
- name: myapp
schemas:
- { name: deprecated_schema, state: absent }
执行的 SQL:
DROP SCHEMA IF EXISTS "deprecated_schema" CASCADE;
删除模式使用 CASCADE 选项,会同时删除模式内的所有对象(表、视图、函数等)。
请确保理解影响范围后再执行删除操作。
扩展通过 extensions 数组配置,支持安装和卸载操作。
- name: myapp
extensions:
# 简单形式:只指定扩展名
- postgis
- pg_trgm
# 完整形式:指定 schema 和版本
- { name: vector, schema: public }
- { name: pg_stat_statements, schema: monitor, version: '1.10' }
执行的 SQL:
CREATE EXTENSION IF NOT EXISTS "postgis" CASCADE;
CREATE EXTENSION IF NOT EXISTS "pg_trgm" CASCADE;
CREATE EXTENSION IF NOT EXISTS "vector" WITH SCHEMA "public" CASCADE;
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" WITH SCHEMA "monitor" VERSION '1.10' CASCADE;
使用 state: absent 标记要卸载的扩展:
- name: myapp
extensions:
- { name: pg_trgm, state: absent } # 卸载扩展
- { name: postgis } # 保留扩展
执行的 SQL:
DROP EXTENSION IF EXISTS "pg_trgm" CASCADE;
CREATE EXTENSION IF NOT EXISTS "postgis" CASCADE;
卸载扩展使用 CASCADE 选项,会同时删除依赖该扩展的所有对象(视图、函数等)。
请确保理解影响范围后再执行卸载操作。
要删除数据库,将其 state 设置为 absent 并执行剧本:
pg_databases:
- name: olddb
state: absent
bin/pgsql-db <cls> olddb
删除操作会:
is_template: true,先执行 ALTER DATABASE ... IS_TEMPLATE falseDROP DATABASE ... WITH (FORCE) 强制删除数据库(PG13+)保护机制:
postgres、template0、template1 无法删除删除数据库是不可逆操作,会永久删除该数据库中的所有数据。 执行前请确保:
recreate 状态用于重建数据库,等效于先删除再创建:
pg_databases:
- name: testdb
state: recreate
owner: dbuser_test
baseline: test_init.sql # 重建后执行初始化
bin/pgsql-db <cls> testdb
适用场景:
与手动 DROP + CREATE 的区别:
baseline 初始化脚本可以使用现有数据库作为模板创建新数据库,实现数据库结构的快速复制。
pg_databases:
# 1. 首先定义模板数据库
- name: app_template
owner: dbuser_app
schemas: [core, api]
extensions: [postgis, pg_trgm]
baseline: app_schema.sql
# 2. 使用模板创建业务数据库
- name: app_prod
template: app_template
owner: dbuser_app
- name: app_staging
template: app_template
strategy: FILE_COPY # 或 WAL_LOG
owner: dbuser_app
| 策略 | 说明 | 适用场景 |
|---|---|---|
FILE_COPY | 直接复制数据文件 | 大模板,通用场景 |
WAL_LOG | 通过 WAL 日志复制 | 小模板,不阻塞模板连接 |
当使用非系统模板(非 template0/template1)时,Pigsty 会自动终止模板数据库上的连接以允许克隆。
- name: new_db
template: existing_db # 使用现有业务数据库作为模板
owner: dbuser_app
默认只有超级用户或数据库所有者可以使用普通数据库作为模板。
使用 is_template: true 可以允许任何有 CREATEDB 权限的用户克隆:
- name: shared_template
is_template: true # 允许任何有 CREATEDB 权限的用户克隆
owner: dbuser_app
使用 icu 本地化提供者时,必须指定 template: template0:
- name: myapp_icu
template: template0 # 必须使用 template0
locale_provider: icu
icu_locale: en-US
encoding: UTF8
默认情况下,所有业务数据库都会添加到 Pgbouncer 连接池中。
- name: myapp
pgbouncer: true # 是否加入连接池(默认 true)
pool_mode: transaction # 池化模式:transaction/session/statement
pool_size: 64 # 默认池大小
pool_size_min: 0 # 最小池大小
pool_reserve: 32 # 保留连接数
pool_connlimit: 100 # 最大数据库连接
pool_auth_user: dbuser_meta # 认证查询用户
配置文件位于 /etc/pgbouncer/database.txt:
myapp = host=/var/run/postgresql pool_mode=transaction pool_size=64
某些内部数据库可能不需要通过连接池访问:
- name: internal_db
pgbouncer: false # 不加入连接池
| 模式 | 说明 | 适用场景 |
|---|---|---|
transaction | 事务结束后归还连接(默认) | 大多数 OLTP 应用 |
session | 会话结束后归还连接 | 需要会话状态的应用 |
statement | 语句结束后归还连接 | 无状态查询 |
PostgreSQL 15+ 引入了 locale_provider 参数,支持不同的本地化实现。
- name: myapp_icu
template: template0 # ICU 必须使用 template0
locale_provider: icu
icu_locale: en-US # ICU 本地化规则
encoding: UTF8
- name: myapp_builtin
template: template0
locale_provider: builtin
builtin_locale: C.UTF-8 # 内置本地化规则
encoding: UTF8
- name: myapp_custom_icu
template: template0
locale_provider: icu
icu_locale: en-US
icu_rules: '&V << w <<< W' # 自定义 ICU 排序规则
| 提供者 | 版本要求 | 特点 |
|---|---|---|
libc | - | 传统方式,依赖操作系统 |
icu | PG15+ | 跨平台一致,功能丰富 |
builtin | PG17+ | 最高效的 C/C.UTF-8 排序 |
| 操作 | 命令 |
|---|---|
| 创建数据库 | bin/pgsql-db <cls> <dbname> |
| 修改数据库 | bin/pgsql-db <cls> <dbname> |
| 删除数据库 | 设置 state: absent 后执行 bin/pgsql-db <cls> <dbname> |
| 重建数据库 | 设置 state: recreate 后执行 bin/pgsql-db <cls> <dbname> |
| 查看数据库列表 | psql -c '\l' |
| 查看连接池数据库 | cat /etc/pgbouncer/database.txt |
# 创建基本数据库
- name: myapp
owner: dbuser_myapp
comment: my application database
# 创建带扩展的数据库
- name: geodata
owner: dbuser_geo
extensions: [postgis, postgis_topology]
# 限制连接的私有数据库
- name: secure_db
owner: dbuser_secure
revokeconn: true
connlimit: 10
# 设置数据库级参数
- name: analytics
owner: dbuser_analyst
parameters:
work_mem: '512MB'
statement_timeout: '5min'
# 使用 ICU 本地化
- name: i18n_db
template: template0
locale_provider: icu
icu_locale: zh-Hans
encoding: UTF8
# 删除数据库
- name: old_db
state: absent
# 重建数据库
- name: test_db
state: recreate
baseline: test_init.sql
bin/pgsql-db 执行时会依次:
关于数据库的访问权限,请参考 ACL:数据库权限 一节。
HBA 规则的变更需要重新渲染配置文件并重载服务。本文介绍 HBA 规则的日常管理操作。
| 操作 | 命令 |
|---|---|
| 刷新集群 HBA | bin/pgsql-hba <cls> |
| 刷新特定实例 | bin/pgsql-hba <cls> <ip>... |
| 仅刷新 PostgreSQL | ./pgsql.yml -l <cls> -t pg_hba,pg_reload |
| 仅刷新 Pgbouncer | ./pgsql.yml -l <cls> -t pgbouncer_hba,pgbouncer_reload |
| 查看当前 HBA | psql -c "TABLE pg_hba_file_rules" |
| 验证 HBA 配置 | psql -c "SELECT pg_reload_conf()" |
修改 pigsty.yml 中的 HBA 规则后,需要重新渲染配置文件并让服务重载。
推荐使用 bin/pgsql-hba 脚本,一键完成 PostgreSQL 和 Pgbouncer 的 HBA 刷新:
# 刷新整个集群的 HBA 规则
bin/pgsql-hba pg-meta
# 刷新特定实例(多个 IP 空格分隔)
bin/pgsql-hba pg-meta 10.10.10.10
bin/pgsql-hba pg-meta 10.10.10.11 10.10.10.12
# 查看脚本帮助
bin/pgsql-hba --help
脚本内部执行:
./pgsql.yml -l <cluster> -t pg_hba,pg_reload,pgbouncer_hba,pgbouncer_reload
直接使用 pgsql.yml playbook 的相关 tags:
# 刷新 PostgreSQL HBA 并重载
./pgsql.yml -l pg-meta -t pg_hba,pg_reload
# 刷新 Pgbouncer HBA 并重载
./pgsql.yml -l pg-meta -t pgbouncer_hba,pgbouncer_reload
# 同时刷新两者
./pgsql.yml -l pg-meta -t pg_hba,pg_reload,pgbouncer_hba,pgbouncer_reload
# 使用额外变量强制重载
./pgsql.yml -l pg-meta -e pg_reload=true -t pg_hba,pg_reload
| Tag | 说明 |
|---|---|
pg_hba | 渲染 PostgreSQL HBA 配置文件 |
pg_reload | 重载 PostgreSQL 配置(需配合 pg_reload=true) |
pgbouncer_hba | 渲染 Pgbouncer HBA 配置文件 |
pgbouncer_reload | 重载 Pgbouncer 配置 |
HBA 配置文件由 Ansible 渲染生成:
| 服务 | 配置文件路径 | 模板文件 |
|---|---|---|
| PostgreSQL | /pg/data/pg_hba.conf | roles/pgsql/templates/pg_hba.conf |
| Pgbouncer | /etc/pgbouncer/pgb_hba.conf | roles/pgsql/templates/pgbouncer.hba |
警告:不要直接编辑这些文件,下次执行 playbook 时会被覆盖。所有变更应在
pigsty.yml中进行。
# 使用 psql 查看 PostgreSQL HBA 规则
psql -c "TABLE pg_hba_file_rules"
# 或者直接查看配置文件
cat /pg/data/pg_hba.conf
# 查看 Pgbouncer HBA 规则
cat /etc/pgbouncer/pgb_hba.conf
# PostgreSQL 重载配置(会验证语法)
psql -c "SELECT pg_reload_conf()"
# 如果有语法错误,查看日志
tail -f /pg/log/postgresql-*.log
# 测试特定用户从特定地址的连接
psql -h <host> -p 5432 -U <user> -d <database> -c "SELECT 1"
# 查看连接被哪条 HBA 规则匹配
psql -c "SELECT * FROM pg_hba_file_rules WHERE database @> ARRAY['<dbname>']::text[]"
pigsty.yml,在集群的 pg_hba_rules 中添加规则:pg-meta:
vars:
pg_hba_rules:
- {user: new_user, db: new_db, addr: '192.168.1.0/24', auth: pwd, title: 'new app access'}
bin/pgsql-hba pg-meta
当发现恶意 IP 时,可以快速添加黑名单规则:
order: 0)的拒绝规则:pg_hba_rules:
- {user: all, db: all, addr: '10.1.1.100/32', auth: deny, order: 0, title: 'emergency block'}
bin/pgsql-hba pg-meta
为主库和从库配置不同的 HBA 规则:
pg_hba_rules:
# 仅主库允许写入用户
- {user: writer, db: all, addr: intra, auth: pwd, role: primary, title: 'writer on primary'}
# 从库允许只读用户
- {user: reader, db: all, addr: world, auth: ssl, role: replica, title: 'reader on replica'}
执行刷新后,规则会根据实例的 pg_role 自动启用或禁用。
当集群新增实例后,使用 addr: cluster 的规则需要刷新才能包含新成员:
# 扩容新实例
./pgsql.yml -l 10.10.10.14
# 刷新所有实例的 HBA(包含新成员 IP)
bin/pgsql-hba pg-meta
Patroni 故障转移后,实例的 pg_role 可能与配置不一致。如果 HBA 规则使用了 role 过滤,需要:
pigsty.yml 中的角色定义# 更新配置文件中的角色后刷新
bin/pgsql-hba pg-meta
症状:FATAL: no pg_hba.conf entry for host "x.x.x.x", user "xxx", database "xxx"
排查步骤:
psql -c "TABLE pg_hba_file_rules"
确认客户端 IP、用户名、数据库是否匹配任何规则
检查规则顺序(首条匹配生效)
添加对应规则并刷新
症状:FATAL: password authentication failed for user "xxx"
排查步骤:
pg_pwd_enc)与客户端兼容性\du 或 SELECT * FROM pg_roles WHERE rolname = 'xxx'排查步骤:
psql -c "SELECT pg_reload_conf()"
head -20 /pg/data/pg_hba.conf
HBA 是首条匹配生效,如果规则未按预期工作:
order 值psql -c "TABLE pg_hba_file_rules" 查看实际顺序order 值或规则位置虽然可以直接编辑 /pg/data/pg_hba.conf 并重载,但不推荐这样做:
# 直接编辑(不推荐)
vi /pg/data/pg_hba.conf
# 重载配置
psql -c "SELECT pg_reload_conf()"
# 或
pg_ctl reload -D /pg/data
# 或
systemctl reload postgresql
问题:下次执行 Ansible playbook 时,手动修改会被覆盖。
正确做法:始终在 pigsty.yml 中修改,然后执行 bin/pgsql-hba 刷新。
Pgbouncer 的 HBA 管理与 PostgreSQL 类似,但有一些差异:
/etc/pgbouncer/pgb_hba.confdb: replicationpeer 而非 ident# 仅刷新 Pgbouncer HBA
./pgsql.yml -l pg-meta -t pgbouncer_hba,pgbouncer_reload
# 或使用统一脚本(同时刷新 PostgreSQL 和 Pgbouncer)
bin/pgsql-hba pg-meta
cat /etc/pgbouncer/pgb_hba.conf
pg_hba.conf,所有变更通过 pigsty.ymlorder: 0,确保优先匹配addr: world + auth: trustpg_stat_activity 中的认证失败记录pigsty.yml# 刷新 HBA(推荐)
bin/pgsql-hba <cluster>
# 查看 PostgreSQL HBA
psql -c "TABLE pg_hba_file_rules"
cat /pg/data/pg_hba.conf
# 查看 Pgbouncer HBA
cat /etc/pgbouncer/pgb_hba.conf
# 重载 PostgreSQL 配置
psql -c "SELECT pg_reload_conf()"
# 测试连接
psql -h <host> -U <user> -d <db> -c "SELECT 1"
# 查看认证失败日志
tail -f /pg/log/postgresql-*.log | grep -i auth
本文整理了 Pigsty 中常用的 PostgreSQL 管理预案,用于维护生产环境中的数据库集群。
以下是常见 PostgreSQL 管理任务的标准操作程序:
PGSQL 剧本与快捷方式:
bin/pgsql-add <cls> # 创建 pgsql 集群 <cls>
bin/pgsql-user <cls> <username> # 在 <cls> 上创建 pg 用户 <username>
bin/pgsql-db <cls> <dbname> # 在 <cls> 上创建 pg 数据库 <dbname>
bin/pgsql-svc <cls> [...ip] # 重新加载集群 <cls> 的 pg 服务
bin/pgsql-hba <cls> [...ip] # 重新加载集群 <cls> 的 postgres/pgbouncer HBA 规则
bin/pgsql-add <cls> [...ip] # 为集群 <cls> 添加从库副本
bin/pgsql-rm <cls> [...ip] # 从集群 <cls> 移除实例
bin/pgsql-rm <cls> # 删除 pgsql 集群 <cls>
Patroni 管理命令与快捷方式:
pg list <cls> # 打印集群信息
pg edit-config <cls> # 编辑集群配置
pg reload <cls> [ins] # 重新加载集群配置
pg restart <cls> [ins] # 重启 PostgreSQL 集群
pg reinit <cls> [ins] # 重新初始化集群成员
pg pause <cls> # 进入维护模式(自动故障转移暂停)
pg resume <cls> # 退出维护模式
pg switchover <cls> # 在集群 <cls> 上进行主动主从切换(主库健康)
pg failover <cls> # 在集群 <cls> 上进行故障转移(主库故障)
pgBackRest 备份/恢复命令与快捷方式:
pb info # 打印 pgbackrest 备份仓库信息
pg-backup # 进行备份,默认进行增量备份,如果没有完整备份过就做全量备份
pg-backup full # 进行全量备份
pg-backup diff # 进行差异备份
pg-backup incr # 进行增量备份
pg-pitr -i # 恢复到最近备份完成的时间(不常用)
pg-pitr --time="2022-12-30 14:44:44+08" # 恢复到特定时间点(如在删除数据库或表的情况下)
pg-pitr --name="my-restore-point" # 恢复到由 pg_create_restore_point 创建的命名还原点
pg-pitr --lsn="0/7C82CB8" -X # 恢复到 LSN 之前
pg-pitr --xid="1234567" -X -P # 恢复到特定的事务ID之前,然后将其提升为主库
pg-pitr --backup=latest # 恢复到最新的备份集
pg-pitr --backup=20221108-105325 # 恢复到特定的备份集,使用名称指定,可以使用 pgbackrest info 进行检查
使用 Systemd 管理系统组件的命令:
systemctl stop patroni # 启动 停止 重启 重载
systemctl stop pgbouncer # 启动 停止 重启 重载
systemctl stop pg_exporter # 启动 停止 重启 重载
systemctl stop pgbouncer_exporter # 启动 停止 重启 重载
systemctl stop node_exporter # 启动 停止 重启
systemctl stop haproxy # 启动 停止 重启 重载
systemctl stop vip-manager # 启动 停止 重启 重载
systemctl stop postgres # 仅当 patroni_mode == 'remove' 时使用这个服务
要创建一个新的Postgres集群,请首先在配置清单中定义,然后进行初始化:
bin/node-add <cls> # 为集群 <cls> 初始化节点 # ./node.yml -l <cls>
bin/pgsql-add <cls> # 初始化集群 <cls> 的pgsql实例 # ./pgsql.yml -l <cls>
请注意,PGSQL 模块需要在 Pigsty 纳管的节点上安装,请先使用
bin/node-add纳管节点。
要在现有的Postgres集群上创建一个新的业务用户,请将用户定义添加到 all.children.<cls>.pg_users,然后使用以下命令将其创建:
bin/pgsql-user <cls> <username> # ./pgsql-user.yml -l <cls> -e username=<username>
要从现有的Postgres集群上安全删除一个用户,请将用户定义中的 state 设置为 absent,然后执行:
bin/pgsql-user <cls> <username> # ./pgsql-user.yml -l <cls> -e username=<username>
Pigsty 使用 pg-drop-role 脚本安全删除用户,会自动处理所有依赖对象:
pg-meta:
vars:
pg_users:
- name: dbuser_old
state: absent # 标记用户为删除状态
bin/pgsql-user pg-meta dbuser_old
您也可以直接在数据库节点上使用 pg-drop-role 脚本手动删除用户:
# 检查用户依赖关系(只读操作)
sudo -u postgres pg-drop-role dbuser_old --check
# 预览删除操作(不实际执行)
sudo -u postgres pg-drop-role dbuser_old --dry-run -v
# 执行删除(强制终止活跃连接)
sudo -u postgres pg-drop-role dbuser_old --force
删除流程:
/tmp/pg_drop_role_<user>_<timestamp>.logALTER ROLE ... NOLOGIN--force 时终止活跃连接postgresREASSIGN OWNED + DROP OWNEDDROP ROLE以下系统用户无法通过 state: absent 删除,会被自动跳过:
postgres(超级用户)replicator(或 pg_replication_username 配置的用户)dbuser_dba(或 pg_admin_username 配置的用户)dbuser_monitor(或 pg_monitor_username 配置的用户)要在现有的Postgres集群上创建一个新的数据库用户,请将数据库定义添加到 all.children.<cls>.pg_databases,然后按照以下方式创建数据库:
bin/pgsql-db <cls> <dbname> # ./pgsql-db.yml -l <cls> -e dbname=<dbname>
注意:如果数据库指定了一个非默认的属主,该属主用户应当已存在,否则您必须先创建用户。
服务是 PostgreSQL 对外提供能力的访问点(PGURL可达),由主机节点上的 HAProxy 对外暴露。
当集群成员发生变化时使用此任务,例如:添加/移除副本,主从切换/故障转移 / 暴露新服务,或更新现有服务的配置(例如,LB权重)
要在整个代理集群,或特定实例上创建新服务或重新加载现有服务:
bin/pgsql-svc <cls> # pgsql.yml -l <cls> -t pg_service -e pg_reload=true
bin/pgsql-svc <cls> [ip...] # pgsql.yml -l ip... -t pg_service -e pg_reload=true
当您的 Postgres/Pgbouncer HBA 规则发生更改时,您 可能 需要重载 HBA 以应用更改。
如果您有任何特定于角色的 HBA 规则,或者在IP地址段中引用了集群成员的别名,那么当主从切换/集群扩缩容后也可能需要重载HBA。
要在整个集群或特定实例上重新加载 postgres 和 pgbouncer 的 HBA 规则:
bin/pgsql-hba <cls> # pgsql.yml -l <cls> -t pg_hba,pg_reload,pgbouncer_hba,pgbouncer_reload -e pg_reload=true
bin/pgsql-hba <cls> [ip...] # pgsql.yml -l ip... -t pg_hba,pg_reload,pgbouncer_hba,pgbouncer_reload -e pg_reload=true
要更改现有的 Postgres 集群配置,您需要在管理节点上使用管理员用户(安装Pigsty的用户,nopass ssh/sudo)发起控制命令:
另一种方式是在数据库集群中的任何节点上,使用 dbsu (默认为 postgres) ,也可以执行管理命令,但只能管理本集群。
pg edit-config <cls> # interactive config a cluster with patronictl
更改 patroni 参数和 postgresql.parameters,根据提示保存并应用更改即可。
您可以跳过交互模式,并使用 -p 选项覆盖 postgres 参数,例如:
pg edit-config -p log_min_duration_statement=1000 pg-test
pg edit-config --force -p shared_preload_libraries='timescaledb, pg_cron, pg_stat_statements, auto_explain'
您还可以使用 Patroni REST API 以非交互式方式更改配置,例如:
$ curl -s 10.10.10.11:8008/config | jq . # get current config
$ curl -u 'postgres:Patroni.API' \
-d '{"postgresql":{"parameters": {"log_min_duration_statement":200}}}' \
-s -X PATCH http://10.10.10.11:8008/config | jq .
注意:Patroni 敏感API(例如重启等) 访问仅限于从基础设施/管理节点发起,并且有 HTTP 基本认证(用户名/密码)以及可选的 HTTPS 保护。
若要将新从库添加到现有的 PostgreSQL 集群中,您需要将其定义添加到配置清单:all.children.<cls>.hosts 中,然后:
bin/node-add <ip> # 将节点 <ip> 纳入 Pigsty 管理
bin/pgsql-add <cls> <ip> # 初始化 <ip> ,作为集群 <cls> 的新从库
这将会把节点 <ip> 添加到 pigsty 并将其初始化为集群 <cls> 的一个副本。
集群服务将会重新加载以接纳新成员。
例如,如果您想将 pg-test-3 / 10.10.10.13 添加到现有的集群 pg-test,您首先需要更新配置清单:
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 } # <--- 新成员
vars: { pg_cluster: pg-test }
然后按如下方式应用更改:
bin/node-add 10.10.10.13 # 将节点添加到 pigsty
bin/pgsql-add pg-test 10.10.10.13 # 在 10.10.10.13 上为集群 pg-test 初始化新的副本
这与集群初始化相似,但只在单个实例上工作:
[ OK ] 初始化实例 10.10.10.11 到 pgsql 集群 'pg-test' 中:
[WARN] 提醒:先将节点添加到 pigsty 中,然后再安装模块 'pgsql'
[HINT] $ bin/node-add 10.10.10.11 # 除 infra 节点外,先运行此命令
[WARN] 从集群初始化实例:
[ OK ] $ ./pgsql.yml -l '10.10.10.11,&pg-test'
[WARN] 重新加载现有实例上的 pg_service:
[ OK ] $ ./pgsql.yml -l 'pg-test,!10.10.10.11' -t pg_service
若要从现有的 PostgreSQL 集群中移除副本:
bin/pgsql-rm <cls> <ip...> # ./pgsql-rm.yml -l <ip>
这将从集群 <cls> 中移除实例 <ip>。 集群服务将会重新加载以从负载均衡器中踢除已移除的实例。
例如,如果您想从现有的集群 pg-test 中移除 pg-test-3 / 10.10.10.13:
bin/pgsql-rm pg-test 10.10.10.13 # 从 pg-test 中移除 pgsql 实例 10.10.10.13
bin/node-rm 10.10.10.13 # 从 pigsty 中移除该节点(可选)
vi pigsty.yml # 从目录中移除实例定义
bin/pgsql-svc pg-test # 刷新现有实例上的 pg_service,以从负载均衡器中踢除已移除的实例
[ OK ] 从 'pg-test' 移除 10.10.10.13 的 pgsql 实例:
[WARN] 从集群中移除实例:
[ OK ] $ ./pgsql-rm.yml -l '10.10.10.13,&pg-test'
并从配置清单中移除实例定义:
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 } # <--- 执行后移除此行
vars: { pg_cluster: pg-test }
最后,您可以重载PG服务并从负载均衡器中踢除已移除的实例:
bin/pgsql-svc pg-test # 重载 pg-test 上的服务
要移除整个 Postgres 集群,只需运行:
bin/pgsql-rm <cls> # ./pgsql-rm.yml -l <cls>
注意:如果为这个集群配置了pg_safeguard(或全局设置为 true),pgsql-rm.yml 将中止,以避免意外移除集群。
您可以使用 playbook 命令行参数明确地覆盖它,以强制执行清除:
./pgsql-rm.yml -l pg-meta -e pg_safeguard=false # 强制移除 pg 集群 pg-meta
您可以使用 patroni 命令行工具执行 PostgreSQL 集群的切换操作。
pg switchover <cls> # 交互模式,您可以使用下面的参数组合直接跳过此交互向导
pg switchover --leader pg-test-1 --candidate=pg-test-2 --scheduled=now --force pg-test
$ pg switchover pg-test
Master [pg-test-1]:
Candidate ['pg-test-2', 'pg-test-3'] []: pg-test-2
When should the switchover take place (e.g. 2022-12-26T07:39 ) [now]: now
Current cluster topology
+ Cluster: pg-test (7181325041648035869) -----+----+-----------+-----------------+
| Member | Host | Role | State | TL | Lag in MB | Tags |
+-----------+-------------+---------+---------+----+-----------+-----------------+
| pg-test-1 | 10.10.10.11 | Leader | running | 1 | | clonefrom: true |
| | | | | | | conf: tiny.yml |
| | | | | | | spec: 1C.2G.50G |
| | | | | | | version: '15' |
+-----------+-------------+---------+---------+----+-----------+-----------------+
| pg-test-2 | 10.10.10.12 | Replica | running | 1 | 0 | clonefrom: true |
| | | | | | | conf: tiny.yml |
| | | | | | | spec: 1C.2G.50G |
| | | | | | | version: '15' |
+-----------+-------------+---------+---------+----+-----------+-----------------+
| pg-test-3 | 10.10.10.13 | Replica | running | 1 | 0 | clonefrom: true |
| | | | | | | conf: tiny.yml |
| | | | | | | spec: 1C.2G.50G |
| | | | | | | version: '15' |
+-----------+-------------+---------+---------+----+-----------+-----------------+
Are you sure you want to switchover cluster pg-test, demoting current master pg-test-1? [y/N]: y
2022-12-26 06:39:58.02468 Successfully switched over to "pg-test-2"
+ Cluster: pg-test (7181325041648035869) -----+----+-----------+-----------------+
| Member | Host | Role | State | TL | Lag in MB | Tags |
+-----------+-------------+---------+---------+----+-----------+-----------------+
| pg-test-1 | 10.10.10.11 | Replica | stopped | | unknown | clonefrom: true |
| | | | | | | conf: tiny.yml |
| | | | | | | spec: 1C.2G.50G |
| | | | | | | version: '15' |
+-----------+-------------+---------+---------+----+-----------+-----------------+
| pg-test-2 | 10.10.10.12 | Leader | running | 1 | | clonefrom: true |
| | | | | | | conf: tiny.yml |
| | | | | | | spec: 1C.2G.50G |
| | | | | | | version: '15' |
+-----------+-------------+---------+---------+----+-----------+-----------------+
| pg-test-3 | 10.10.10.13 | Replica | running | 1 | 0 | clonefrom: true |
| | | | | | | conf: tiny.yml |
| | | | | | | spec: 1C.2G.50G |
| | | | | | | version: '15' |
+-----------+-------------+---------+---------+----+-----------+-----------------+
要通过 Patroni API 来执行此操作(例如,在指定时间将主库从 2号实例 切换到 1号实例)
curl -u 'postgres:Patroni.API' \
-d '{"leader":"pg-test-2", "candidate": "pg-test-1","scheduled_at":"2022-12-26T14:47+08"}' \
-s -X POST http://10.10.10.11:8008/switchover
无论是主动切换还是故障切换,您都需要在集群成员身份发生变化后,重新刷新服务与HBA规则。您应当在变更发生后及时(例如几个小时,一天内),完成此操作:
bin/pgsql-svc <cls>
bin/pgsql-hba <cls>
使用 pgBackRest 创建备份,需要以本地 dbsu (默认为 postgres)的身份运行以下命令:
pg-backup # 执行备份,如有必要,执行增量或全量备份
pg-backup full # 执行全量备份
pg-backup diff # 执行差异备份
pg-backup incr # 执行增量备份
pb info # 打印备份信息 (pgbackrest info)
参阅备份恢复获取更多信息。
您可以将 crontab 添加到 node_crontab 以指定您的备份策略。
# 每天凌晨1点做一次全备份
- '00 01 * * * postgres /pg/bin/pg-backup full'
# 周一凌晨1点进全量备份,其他工作日进行增量备份
- '00 01 * * 1 postgres /pg/bin/pg-backup full'
- '00 01 * * 2,3,4,5,6,7 postgres /pg/bin/pg-backup'
要将集群恢复到先前的时间点 (PITR),请以本地 dbsu 用户(默认为postgres)运行 Pigsty 提供的辅助脚本 pg-pitr
pg-pitr -i # 恢复到最近备份完成的时间(不常用)
pg-pitr --time="2022-12-30 14:44:44+08" # 恢复到指定的时间点(在删除数据库或表的情况下使用)
pg-pitr --name="my-restore-point" # 恢复到使用 pg_create_restore_point 创建的命名恢复点
pg-pitr --lsn="0/7C82CB8" -X # 在LSN之前立即恢复
pg-pitr --xid="1234567" -X -P # 在指定的事务ID之前立即恢复,然后将集群直接提升为主库
pg-pitr --backup=latest # 恢复到最新的备份集
pg-pitr --backup=20221108-105325 # 恢复到特定备份集,备份集可以使用 pgbackrest info 列出
该命令会输出操作手册,请按照说明进行操作。查看备份恢复-PITR获取详细信息。
# 恢复到最新可用的点(例如硬件故障)
pgbackrest --stanza=pg-meta restore
# PITR 到特定的时间点(例如意外删除表)
pgbackrest --stanza=pg-meta --type=time --target="2022-11-08 10:58:48" \
--target-action=promote restore
# 恢复特定的备份点,然后提升(或暂停|关闭)
pgbackrest --stanza=pg-meta --type=immediate --target-action=promote \
--set=20221108-105325F_20221108-105938I restore
要添加新版本的 RPM 包,你需要将它们加入到 repo_packages 和 repo_url_packages 中。
使用 ./infra.yml -t repo_build 子任务在 Infra 节点上重新构建本地软件仓库。然后,你可以使用 ansible 的 package 模块安装这些包:
ansible pg-test -b -m package -a "name=pg_cron_15,topn_15,pg_stat_monitor_15*" # 使用 ansible 安装一些包
# 在基础设施/管理节点上添加上游软件仓库,然后手工下载所需的软件包
cd ~/pigsty; ./infra.yml -t repo_upstream,repo_cache # 添加上游仓库(互联网)
cd /www/pigsty; repotrack "some_new_package_name" # 下载最新的 RPM 包
# 更新本地软件仓库元数据
cd ~/pigsty; ./infra.yml -t repo_create # 重新创建本地软件仓库
./node.yml -t node_repo # 刷新所有节点上的 YUM/APT 缓存
# 也可以使用 Ansible 手工刷新节点上的 YUM/APT 缓存
ansible all -b -a 'yum clean all' # 清理节点软件仓库缓存
ansible all -b -a 'yum makecache' # 从新的仓库重建yum/apt缓存
ansible all -b -a 'apt clean' # 清理 APT 缓存(Ubuntu/Debian)
ansible all -b -a 'apt update' # 重建 APT 缓存(Ubuntu/Debian)
例如,你可以使用以下方式安装或升级包:
ansible pg-test -b -m package -a "name=postgresql15* state=latest"
如果你想在 PostgreSQL 集群上安装扩展,请将它们加入到 pg_extensions 中,并执行:
./pgsql.yml -t pg_extension # 安装扩展
一部分扩展需要在 shared_preload_libraries 中加载后才能生效。你可以将它们加入到 pg_libs 中,或者配置一个已有的集群。
最后,在集群的主库上执行 CREATE EXTENSION <extname>; 来完成扩展的安装。
ansible pg-test -b -m package -a "name=pg_cron_15" # 在所有节点上安装 pg_cron 包
# 将 pg_cron 添加到 shared_preload_libraries 中
pg edit-config --force -p shared_preload_libraries='timescaledb, pg_cron, pg_stat_statements, auto_explain'
pg restart --force pg-test # 重新启动集群
psql -h pg-test -d postgres -c 'CREATE EXTENSION pg_cron;' # 在主库上安装 pg_cron
更多细节,请参考PGSQL扩展安装。
要执行小版本的服务器升级/降级,您首先需要在本地软件仓库中添加软件:最新的PG小版本 RPM/DEB。
首先对所有从库执行滚动升级/降级,然后执行集群主从切换以升级/降级主库。
ansible <cls> -b -a "yum upgrade/downgrade -y <pkg>" # 升级/降级软件包
pg restart --force <cls> # 重启集群
将15.1的包添加到软件仓库并刷新节点的 yum/apt 缓存:
cd ~/pigsty; ./infra.yml -t repo_upstream # 添加上游仓库
cd /www/pigsty; repotrack postgresql15-*-15.1 # 将15.1的包添加到yum仓库
cd ~/pigsty; ./infra.yml -t repo_create # 重建仓库元数据
ansible pg-test -b -a 'yum clean all' # 清理节点仓库缓存
ansible pg-test -b -a 'yum makecache' # 从新仓库重新生成yum缓存
# 对于 Ubutnu/Debian 用户,使用 apt 替换 yum
ansible pg-test -b -a 'apt clean' # 清理节点仓库缓存
ansible pg-test -b -a 'apt update' # 从新仓库重新生成apt缓存
执行降级并重启集群:
ansible pg-test -b -a "yum downgrade -y postgresql15*" # 降级软件包)
pg restart --force pg-test # 重启整个集群以完成升级
这次我们采用滚动方式升级:
ansible pg-test -b -a "yum upgrade -y postgresql15*" # 升级软件包(或 apt upgrade)
ansible pg-test -b -a '/usr/pgsql/bin/pg_ctl --version' # 检查二进制版本是否为15.2
pg restart --role replica --force pg-test # 重启从库
pg switchover --leader pg-test-1 --candidate=pg-test-2 --scheduled=now --force pg-test # 切换主从
pg restart --role primary --force pg-test # 重启主库
实现大版本升级的最简单办法是:创建一个使用新版本的新集群,然后通过逻辑复制,蓝绿部署,并进行在线迁移。
您也可以进行原地大版本升级,当您只使用数据库内核本身时,这并不复杂,使用 PostgreSQL 自带的 pg_upgrade 即可:
假设您想将 PostgreSQL 大版本从 14 升级到 15,您首先需要在仓库中添加软件,并确保两个大版本两侧安装的核心扩展插件也具有相同的版本号。
./pgsql.yml -t pg_pkg -e pg_version=15 # 安装pg 15的包
sudo su - postgres; mkdir -p /data/postgres/pg-meta-15/data/ # 为15准备目录
pg_upgrade -b /usr/pgsql-14/bin/ -B /usr/pgsql-15/bin/ -d /data/postgres/pg-meta-14/data/ -D /data/postgres/pg-meta-15/data/ -v -c # 预检
pg_upgrade -b /usr/pgsql-14/bin/ -B /usr/pgsql-15/bin/ -d /data/postgres/pg-meta-14/data/ -D /data/postgres/pg-meta-15/data/ --link -j8 -v -c
rm -rf /usr/pgsql; ln -s /usr/pgsql-15 /usr/pgsql; # 修复二进制链接
mv /data/postgres/pg-meta-14 /data/postgres/pg-meta-15 # 重命名数据目录
rm -rf /pg; ln -s /data/postgres/pg-meta-15 /pg # 修复数据目录链接
要创建一个新的Postgres集群,请首先在配置清单中定义,然后进行初始化:
bin/node-add <cls> # 为集群 <cls> 初始化节点 # ./node.yml -l <cls>
bin/pgsql-add <cls> # 初始化集群 <cls> 的pgsql实例 # ./pgsql.yml -l <cls>
请注意,PGSQL 模块需要在 Pigsty 纳管的节点上安装,请先使用
bin/node-add纳管节点。
要在现有的Postgres集群上创建一个新的业务用户,请将用户定义添加到 all.children.<cls>.pg_users,然后使用以下命令将其创建:
bin/pgsql-user <cls> <username> # ./pgsql-user.yml -l <cls> -e username=<username>
要在现有的Postgres集群上创建一个新的数据库用户,请将数据库定义添加到 all.children.<cls>.pg_databases,然后按照以下方式创建数据库:
bin/pgsql-db <cls> <dbname> # ./pgsql-db.yml -l <cls> -e dbname=<dbname>
注意:如果数据库指定了一个非默认的属主,该属主用户应当已存在,否则您必须先创建用户。
服务是 PostgreSQL 对外提供能力的访问点(PGURL可达),由主机节点上的 HAProxy 对外暴露。
当集群成员发生变化时使用此任务,例如:添加/移除副本,主从切换/故障转移 / 暴露新服务,或更新现有服务的配置(例如,LB权重)
要在整个代理集群,或特定实例上创建新服务或重新加载现有服务:
bin/pgsql-svc <cls> # pgsql.yml -l <cls> -t pg_service -e pg_reload=true
bin/pgsql-svc <cls> [ip...] # pgsql.yml -l ip... -t pg_service -e pg_reload=true
当您的 Postgres/Pgbouncer HBA 规则发生更改时,您 可能 需要重载 HBA 以应用更改。
如果您有任何特定于角色的 HBA 规则,或者在IP地址段中引用了集群成员的别名,那么当主从切换/集群扩缩容后也可能需要重载HBA。
要在整个集群或特定实例上重新加载 postgres 和 pgbouncer 的 HBA 规则:
bin/pgsql-hba <cls> # pgsql.yml -l <cls> -t pg_hba,pg_reload,pgbouncer_hba,pgbouncer_reload -e pg_reload=true
bin/pgsql-hba <cls> [ip...] # pgsql.yml -l ip... -t pg_hba,pg_reload,pgbouncer_hba,pgbouncer_reload -e pg_reload=true
要更改现有的 Postgres 集群配置,您需要在管理节点上使用管理员用户(安装Pigsty的用户,nopass ssh/sudo)发起控制命令:
另一种方式是在数据库集群中的任何节点上,使用 dbsu (默认为 postgres) ,也可以执行管理命令,但只能管理本集群。
pg edit-config <cls> # interactive config a cluster with patronictl
更改 patroni 参数和 postgresql.parameters,根据提示保存并应用更改即可。
您可以跳过交互模式,并使用 -p 选项覆盖 postgres 参数,例如:
pg edit-config -p log_min_duration_statement=1000 pg-test
pg edit-config --force -p shared_preload_libraries='timescaledb, pg_cron, pg_stat_statements, auto_explain'
您还可以使用 Patroni REST API 以非交互式方式更改配置,例如:
$ curl -s 10.10.10.11:8008/config | jq . # get current config
$ curl -u 'postgres:Patroni.API' \
-d '{"postgresql":{"parameters": {"log_min_duration_statement":200}}}' \
-s -X PATCH http://10.10.10.11:8008/config | jq .
注意:Patroni 敏感API(例如重启等) 访问仅限于从基础设施/管理节点发起,并且有 HTTP 基本认证(用户名/密码)以及可选的 HTTPS 保护。
若要将新从库添加到现有的 PostgreSQL 集群中,您需要将其定义添加到配置清单:all.children.<cls>.hosts 中,然后:
bin/node-add <ip> # 将节点 <ip> 纳入 Pigsty 管理
bin/pgsql-add <cls> <ip> # 初始化 <ip> ,作为集群 <cls> 的新从库
这将会把节点 <ip> 添加到 pigsty 并将其初始化为集群 <cls> 的一个副本。
集群服务将会重新加载以接纳新成员。
例如,如果您想将 pg-test-3 / 10.10.10.13 添加到现有的集群 pg-test,您首先需要更新配置清单:
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 } # <--- 新成员
vars: { pg_cluster: pg-test }
然后按如下方式应用更改:
bin/node-add 10.10.10.13 # 将节点添加到 pigsty
bin/pgsql-add pg-test 10.10.10.13 # 在 10.10.10.13 上为集群 pg-test 初始化新的副本
这与集群初始化相似,但只在单个实例上工作:
[ OK ] 初始化实例 10.10.10.11 到 pgsql 集群 'pg-test' 中:
[WARN] 提醒:先将节点添加到 pigsty 中,然后再安装模块 'pgsql'
[HINT] $ bin/node-add 10.10.10.11 # 除 infra 节点外,先运行此命令
[WARN] 从集群初始化实例:
[ OK ] $ ./pgsql.yml -l '10.10.10.11,&pg-test'
[WARN] 重新加载现有实例上的 pg_service:
[ OK ] $ ./pgsql.yml -l 'pg-test,!10.10.10.11' -t pg_service
若要从现有的 PostgreSQL 集群中移除副本:
bin/pgsql-rm <cls> <ip...> # ./pgsql-rm.yml -l <ip>
这将从集群 <cls> 中移除实例 <ip>。 集群服务将会重新加载以从负载均衡器中踢除已移除的实例。
例如,如果您想从现有的集群 pg-test 中移除 pg-test-3 / 10.10.10.13:
bin/pgsql-rm pg-test 10.10.10.13 # 从 pg-test 中移除 pgsql 实例 10.10.10.13
bin/node-rm 10.10.10.13 # 从 pigsty 中移除该节点(可选)
vi pigsty.yml # 从目录中移除实例定义
bin/pgsql-svc pg-test # 刷新现有实例上的 pg_service,以从负载均衡器中踢除已移除的实例
[ OK ] 从 'pg-test' 移除 10.10.10.13 的 pgsql 实例:
[WARN] 从集群中移除实例:
[ OK ] $ ./pgsql-rm.yml -l '10.10.10.13,&pg-test'
并从配置清单中移除实例定义:
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 } # <--- 执行后移除此行
vars: { pg_cluster: pg-test }
最后,您可以重载PG服务并从负载均衡器中踢除已移除的实例:
bin/pgsql-svc pg-test # 重载 pg-test 上的服务
要移除整个 Postgres 集群,只需运行:
bin/pgsql-rm <cls> # ./pgsql-rm.yml -l <cls>
注意:如果为这个集群配置了pg_safeguard(或全局设置为 true),pgsql-rm.yml 将中止,以避免意外移除集群。
您可以使用 playbook 命令行参数明确地覆盖它,以强制执行清除:
./pgsql-rm.yml -l pg-meta -e pg_safeguard=false # 强制移除 pg 集群 pg-meta
在 Pigsty 中,用户管理采用 IaC 的风格,先在配置清单中定义,然后执行剧本执行。
执行 pgsql-user.yml 剧本是幂等的。它会将指定集群中的指定用户调整至配置清单中的目标状态。
请注意,用户的所有属性都可以通过重新执行剧本进行修改,剧本是幂等的。
Pigsty通过两个配置参数定义数据库集群中的角色与用户:
pg_default_roles:定义全局统一使用的角色和用户pg_users:在数据库集群层面定义业务用户和角色前者用于定义了整套环境中共用的角色与用户,后者定义单个集群中特有的业务角色与用户。二者形式相同,均为用户定义对象的数组。
下面是 Pigsty 演示环境中默认集群 pg-meta 中的业务用户定义:
pg-meta:
hosts: { 10.10.10.10: { pg_seq: 1, pg_role: primary } }
vars:
pg_cluster: pg-meta
pg_users:
- { name: meta ,baseline: cmdb.sql ,comment: pigsty meta database ,schemas: [pigsty] ,extensions: [{name: postgis, schema: public}, {name: timescaledb}]}
- { name: grafana ,owner: dbuser_grafana ,revokeconn: true ,comment: grafana primary database }
- { name: bytebase ,owner: dbuser_bytebase ,revokeconn: true ,comment: bytebase primary database }
唯一必选的字段是 name,它应该是当前 PostgreSQL 集群中有效且唯一的用户名,其他参数都有合理的默认值。
关于用户定义参数的完整参考,请查阅 用户配置参考。
要在现有的 PostgreSQL 集群上创建新的业务用户,请将用户定义添加到 all.children.<cls>.pg_users,然后执行:
bin/pgsql-user <cls> <username> # 等效于:pgsql-user.yml -l <cls> -e username=<username>
示例:创建名为 dbuser_app 的业务用户
pg-meta:
vars:
pg_users:
- name: dbuser_app
password: DBUser.App
pgbouncer: true
roles: [dbrole_readwrite]
comment: application user for myapp
bin/pgsql-user pg-meta dbuser_app
执行效果:
dbuser_appdbrole_readwrite 角色不建议手工使用 SQL 创建业务用户,特别是需要使用 Pgbouncer 连接池时。
使用 bin/pgsql-user 工具创建用户会自动处理连接池配置。
示例:创建业务用户
修改用户属性可以通过更新配置并重新执行剧本来完成:
bin/pgsql-user <cls> <username> # 幂等操作,可重复执行
不同于数据库,创建用户的剧本总是幂等的。当目标用户已经存在时,Pigsty会修改目标用户的属性使其符合配置。
所有用户属性都可以通过重新执行剧本进行修改:
| 属性 | 说明 | 示例 |
|---|---|---|
password | 用户密码 | password: NewPassword |
login | 是否允许登录 | login: false |
superuser | 是否为超级用户 | superuser: true |
createdb | 是否可创建数据库 | createdb: true |
createrole | 是否可创建角色 | createrole: true |
inherit | 是否继承角色权限 | inherit: false |
replication | 是否可进行复制 | replication: true |
bypassrls | 是否可绕过 RLS | bypassrls: true |
connlimit | 连接数限制 | connlimit: 100 |
expire_in | 过期天数 | expire_in: 30 |
expire_at | 过期日期 | expire_at: '2024-12-31' |
comment | 备注信息 | comment: 新的备注 |
roles | 角色成员(增量操作) | 见管理角色成员 |
parameters | 角色级参数 | 见管理用户参数 |
pgbouncer | 是否加入连接池 | pgbouncer: true |
pool_mode | 连接池模式 | pool_mode: session |
pool_connlimit | 连接池连接限制 | pool_connlimit: 50 |
- name: dbuser_app
password: NewSecretPassword # 修改密码
bin/pgsql-user pg-meta dbuser_app
执行的 SQL:
SET log_statement TO 'none';
ALTER USER "dbuser_app" PASSWORD 'NewSecretPassword';
SET log_statement TO DEFAULT;
注意:密码修改时会临时禁用日志记录,避免密码泄露到日志中。
- name: dbuser_app
createdb: true # 允许创建数据库
connlimit: 50 # 限制连接数
执行的 SQL:
ALTER USER "dbuser_app" CREATEDB;
ALTER USER "dbuser_app" CONNECTION LIMIT 50;
- name: dbuser_app
comment: '应用主账号 - 已更新'
执行的 SQL:
COMMENT ON ROLE "dbuser_app" IS '应用主账号 - 已更新';
要删除用户,将其 state 设置为 absent 并执行剧本:
pg_users:
- name: dbuser_old
state: absent
bin/pgsql-user <cls> dbuser_old
删除操作会:
pg-drop-role 脚本安全删除用户postgres受保护的系统用户:
以下系统用户无法通过 state: absent 删除,会被自动跳过:
postgres(超级用户)replicator(或 pg_replication_username 配置的用户)dbuser_dba(或 pg_admin_username 配置的用户)dbuser_monitor(或 pg_monitor_username 配置的用户)Pigsty 使用 pg-drop-role 脚本安全删除用户,该脚本会:
--force)postgres 用户/tmp/pg_drop_role_<user>_<timestamp>.log 创建审计日志无需手动处理依赖对象,脚本会自动完成。
pg-drop-role 是 Pigsty 提供的安全删除用户脚本,位于 /pg/bin/pg-drop-role。
使用方法:
pg-drop-role <role_name> [successor_role] [options]
常用选项:
| 选项 | 说明 |
|---|---|
--check | 只检查依赖关系,不执行删除 |
--dry-run | 显示将要执行的 SQL,不实际执行 |
--force | 强制终止活跃连接后删除 |
-v, --verbose | 显示详细输出 |
-h, --host | 数据库主机 |
-p, --port | 数据库端口 |
示例:
# 检查用户依赖关系(只读操作)
pg-drop-role dbuser_old --check
# 预览删除操作(不实际执行)
pg-drop-role dbuser_old --dry-run -v
# 删除用户,转移对象给 postgres
pg-drop-role dbuser_old
# 删除用户,转移对象给指定用户
pg-drop-role dbuser_old dbuser_new
# 强制删除(终止活跃连接)
pg-drop-role dbuser_old --force
删除流程:
ALTER ROLE ... NOLOGIN--force 时终止活跃连接REASSIGN OWNED + DROP OWNEDDROP ROLE如果需要手动删除用户,可以直接使用 pg-drop-role 脚本:
# 登录到数据库主机
ssh <host>
# 检查依赖关系
sudo -u postgres pg-drop-role dbuser_old --check
# 执行删除
sudo -u postgres pg-drop-role dbuser_old --force
或使用传统 SQL 方式:
-- 查看用户拥有的对象
SELECT n.nspname as schema, c.relname as name, c.relkind as type
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_roles r ON r.oid = c.relowner
WHERE r.rolname = 'dbuser_old';
-- 转移对象所有权
REASSIGN OWNED BY dbuser_old TO postgres;
-- 删除用户拥有的权限
DROP OWNED BY dbuser_old;
-- 删除用户
DROP ROLE dbuser_old;
角色成员关系通过 roles 数组配置,支持简单格式和扩展格式。
- name: dbuser_app
roles:
- dbrole_readwrite
- pg_read_all_data
执行的 SQL:
GRANT "dbrole_readwrite" TO "dbuser_app";
GRANT "pg_read_all_data" TO "dbuser_app";
使用对象格式可以指定更精细的授权选项:
- name: dbuser_admin
roles:
# 普通授予
- dbrole_readwrite
# 可以将此角色授予其他用户
- { name: dbrole_admin, admin: true }
# PG16+: 不能 SET ROLE 到此角色
- { name: pg_monitor, set: false }
# PG16+: 不自动继承此角色的权限
- { name: pg_execute_server_program, inherit: false }
生成的 SQL(PostgreSQL 16+):
GRANT "dbrole_readwrite" TO "dbuser_admin";
GRANT "dbrole_admin" TO "dbuser_admin" WITH ADMIN TRUE;
GRANT "pg_monitor" TO "dbuser_admin";
REVOKE SET OPTION FOR "pg_monitor" FROM "dbuser_admin";
GRANT "pg_execute_server_program" TO "dbuser_admin";
REVOKE INHERIT OPTION FOR "pg_execute_server_program" FROM "dbuser_admin";
使用 state: absent 撤销角色成员关系:
- name: dbuser_app
roles:
- dbrole_readwrite # 保留此角色
- { name: old_role, state: absent } # 撤销此角色
执行的 SQL:
GRANT "dbrole_readwrite" TO "dbuser_app";
REVOKE "old_role" FROM "dbuser_app";
PostgreSQL 16 引入了更细粒度的角色成员关系控制:
| 选项 | 说明 | 用途 |
|---|---|---|
admin | ADMIN OPTION | 允许将角色授予其他用户 |
set | SET OPTION | 允许 SET ROLE 切换到该角色 |
inherit | INHERIT OPTION | 是否自动继承角色权限 |
注意:set 和 inherit 选项仅在 PostgreSQL 16+ 中有效,在早期版本会被忽略并在生成的 SQL 中添加警告注释。
用户级参数通过 parameters 字典配置,会生成 ALTER USER ... SET 语句。
- name: dbuser_analyst
parameters:
work_mem: '256MB'
statement_timeout: '5min'
search_path: 'analytics,public'
执行的 SQL:
ALTER USER "dbuser_analyst" SET "work_mem" = '256MB';
ALTER USER "dbuser_analyst" SET "statement_timeout" = '5min';
ALTER USER "dbuser_analyst" SET "search_path" = 'analytics,public';
使用特殊值 DEFAULT(大小写不敏感)将参数重置为 PostgreSQL 默认值:
- name: dbuser_app
parameters:
work_mem: DEFAULT # 重置为默认值
statement_timeout: DEFAULT
执行的 SQL:
ALTER USER "dbuser_app" SET "work_mem" = DEFAULT;
ALTER USER "dbuser_app" SET "statement_timeout" = DEFAULT;
| 参数 | 说明 | 示例值 |
|---|---|---|
work_mem | 查询工作内存 | '64MB' |
statement_timeout | 语句超时时间 | '30s' |
lock_timeout | 锁等待超时 | '10s' |
idle_in_transaction_session_timeout | 空闲事务超时 | '10min' |
search_path | Schema 搜索路径 | 'app,public' |
log_statement | 日志记录级别 | 'ddl' |
temp_file_limit | 临时文件大小限制 | '10GB' |
使用 expire_in 设置从当前日期起 N 天后过期:
- name: temp_user
expire_in: 30 # 30 天后过期
执行的 SQL(假设当前日期为 2024-01-15):
ALTER USER "temp_user" VALID UNTIL '2024-02-14';
注意:每次执行剧本时会重新计算过期时间,适合需要定期续期的临时用户。
使用 expire_at 设置固定的过期日期:
- name: contractor_user
expire_at: '2024-12-31' # 指定日期过期
执行的 SQL:
ALTER USER "contractor_user" VALID UNTIL '2024-12-31';
- name: permanent_user
expire_at: 'infinity' # 永不过期
执行的 SQL:
ALTER USER "permanent_user" VALID UNTIL 'infinity';
SELECT rolname, rolvaliduntil
FROM pg_roles
WHERE rolvaliduntil IS NOT NULL
ORDER BY rolvaliduntil;
默认情况下,所有带有 pgbouncer: true 标志的用户都会添加到 Pgbouncer 连接池中。
- name: dbuser_app
password: DBUser.App
pgbouncer: true # 添加到连接池
pool_mode: transaction # 池化模式
pool_connlimit: 50 # 用户最大连接数
执行效果:
/etc/pgbouncer/userlist.txtpool_mode 或 pool_connlimit,添加到 /etc/pgbouncer/useropts.txt用户列表 /etc/pgbouncer/userlist.txt:
"postgres" ""
"dbuser_app" "SCRAM-SHA-256$4096:xxx$yyy:zzz"
"dbuser_view" "SCRAM-SHA-256$4096:xxx$yyy:zzz"
用户选项 /etc/pgbouncer/useropts.txt:
dbuser_dba = pool_mode=session max_user_connections=16
dbuser_monitor = pool_mode=session max_user_connections=8
dbuser_app = pool_mode=transaction max_user_connections=50
| 模式 | 说明 | 适用场景 |
|---|---|---|
transaction | 事务结束后归还连接(默认) | 大多数 OLTP 应用 |
session | 会话结束后归还连接 | 需要会话状态的应用(如 DBA 操作) |
statement | 语句结束后归还连接 | 无状态简单查询 |
当用户设置 state: absent 或 pgbouncer: false 时,会从连接池中移除。
- name: dbuser_old
state: absent # 删除用户时自动从连接池移除
或:
- name: dbuser_internal
pgbouncer: false # 仅从连接池移除,保留用户
如果启用了 pgbouncer_auth_query,可以不在 userlist.txt 中维护用户,而是通过查询数据库动态认证。这种方式适合用户数量多、变动频繁的场景。
| 操作 | 命令 |
|---|---|
| 创建用户 | bin/pgsql-user <cls> <username> |
| 修改用户 | bin/pgsql-user <cls> <username> |
| 删除用户 | 设置 state: absent 后执行 bin/pgsql-user <cls> <username> |
| 查看用户列表 | psql -c '\du' |
| 查看用户详情 | psql -c '\du+ <username>' |
| 查看连接池用户 | cat /etc/pgbouncer/userlist.txt |
| 查看用户选项 | cat /etc/pgbouncer/useropts.txt |
# 创建基本业务用户
- name: dbuser_app
password: DBUser.App
pgbouncer: true
roles: [dbrole_readwrite]
comment: application user
# 创建只读用户
- name: dbuser_readonly
password: DBUser.Readonly
pgbouncer: true
roles: [dbrole_readonly]
# 创建 DBA 用户(使用 session 模式)
- name: dbuser_dba
password: DBUser.DBA
pgbouncer: true
pool_mode: session
roles: [dbrole_admin]
parameters:
log_statement: 'all'
# 创建临时用户(30天后过期)
- name: temp_contractor
password: TempPassword
expire_in: 30
roles: [dbrole_readonly]
# 创建角色(不可登录)
- name: custom_role
login: false
comment: custom role for special permissions
# 创建带高级角色选项的用户(PG16+)
- name: dbuser_admin
password: DBUser.Admin
pgbouncer: true
roles:
- dbrole_readwrite
- { name: dbrole_admin, admin: true }
- { name: pg_monitor, set: false }
# 删除用户
- name: dbuser_old
state: absent
bin/pgsql-user 执行时会依次:
-- 查看所有用户
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit, rolvaliduntil
FROM pg_roles
WHERE rolname NOT LIKE 'pg_%'
ORDER BY rolname;
-- 查看用户的角色成员关系
SELECT r.rolname AS member, g.rolname AS role,
m.admin_option, m.set_option, m.inherit_option
FROM pg_auth_members m
JOIN pg_roles r ON r.oid = m.member
JOIN pg_roles g ON g.oid = m.roleid
WHERE r.rolname = 'dbuser_app';
-- 查看用户级参数设置
SELECT rolname, setconfig
FROM pg_db_role_setting s
JOIN pg_roles r ON r.oid = s.setrole
WHERE s.setdatabase = 0; -- 0 表示用户级设置
-- 查看用户拥有的对象
SELECT n.nspname AS schema, c.relname AS name,
CASE c.relkind
WHEN 'r' THEN 'table'
WHEN 'v' THEN 'view'
WHEN 'm' THEN 'materialized view'
WHEN 'i' THEN 'index'
WHEN 'S' THEN 'sequence'
WHEN 'f' THEN 'foreign table'
END AS type
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pg_roles r ON r.oid = c.relowner
WHERE r.rolname = 'dbuser_app'
AND n.nspname NOT IN ('pg_catalog', 'information_schema');
-- 查看即将过期的用户
SELECT rolname, rolvaliduntil,
rolvaliduntil - CURRENT_TIMESTAMP AS time_remaining
FROM pg_roles
WHERE rolvaliduntil IS NOT NULL
AND rolvaliduntil < CURRENT_TIMESTAMP + INTERVAL '30 days'
ORDER BY rolvaliduntil;
关于用户定义的完整参数参考,请参考 用户配置 一节。
关于用户的访问权限,请参考 ACL:角色权限 一节。
Pigsty 默认提供了四套场景化参数模板,可以通过 pg_conf 参数指定并使用。
tiny.yml:为小节点、虚拟机、小型演示优化(1-8核,1-16GB)oltp.yml:为OLTP工作负载和延迟敏感应用优化(4C8GB+)(默认模板)olap.yml:为OLAP工作负载和吞吐量优化(4C8G+)crit.yml:为数据一致性和关键应用优化(4C8G+)Pigsty 会针对这四种默认场景,采取不同的参数优化策略,如下所示:
Pigsty 默认会检测系统的内存大小,并以此为依据设定最大连接数量与内存相关参数。
pg_max_conn:postgres 最大连接数,auto 将使用不同场景下的推荐值pg_shared_buffer_ratio:内存共享缓冲区比例,默认为 0.25默认情况下,Pigsty 使用 25% 的内存作为 PostgreSQL 共享缓冲区,剩余的 75% 作为操作系统缓存。
默认情况下,如果用户没有设置一个 pg_max_conn 最大连接数,Pigsty 会根据以下规则使用默认值:
其中对于 OLTP 与 CRIT 模版来说,如果服务没有指向 pgbouncer 连接池,而是直接连接 postgres 数据库,最大连接会翻倍至 1000 条。
决定最大连接数后,work_mem 会根据共享内存数量 / 最大连接数计算得到,并限定在 64MB ~ 1GB 的范围内。
{% raw %}
{% if pg_max_conn != 'auto' and pg_max_conn|int >= 20 %}{% set pg_max_connections = pg_max_conn|int %}{% else %}{% if pg_default_service_dest|default('postgres') == 'pgbouncer' %}{% set pg_max_connections = 500 %}{% else %}{% set pg_max_connections = 1000 %}{% endif %}{% endif %}
{% set pg_max_prepared_transactions = pg_max_connections if 'citus' in pg_libs else 0 %}
{% set pg_max_locks_per_transaction = (2 * pg_max_connections)|int if 'citus' in pg_libs or 'timescaledb' in pg_libs else pg_max_connections %}
{% set pg_shared_buffers = (node_mem_mb|int * pg_shared_buffer_ratio|float) | round(0, 'ceil') | int %}
{% set pg_maintenance_mem = (pg_shared_buffers|int * 0.25)|round(0, 'ceil')|int %}
{% set pg_effective_cache_size = node_mem_mb|int - pg_shared_buffers|int %}
{% set pg_workmem = ([ ([ (pg_shared_buffers / pg_max_connections)|round(0,'floor')|int , 64 ])|max|int , 1024])|min|int %}
{% endraw %}
在 PostgreSQL 中,有 4 个与并行查询相关的重要参数,Pigsty 会自动根据当前系统的 CPU 核数进行参数优化。 在所有策略中,总并行进程数量(总预算)通常设置为 CPU 核数 + 8,且保底为 16 个,从而为逻辑复制与扩展预留足够的后台 worker 数量,OLAP 和 TINY 模板根据场景略有不同。
| OLTP | 设置逻辑 | 范围限制 |
|---|---|---|
max_worker_processes | max(100% CPU + 8, 16) | 核数 + 4,保底 12, |
max_parallel_workers | max(ceil(50% CPU), 2) | 1/2 CPU 上取整,最少两个 |
max_parallel_maintenance_workers | max(ceil(33% CPU), 2) | 1/3 CPU 上取整,最少两个 |
max_parallel_workers_per_gather | min(max(ceil(20% CPU), 2),8) | 1/5 CPU 下取整,最少两个,最多 8 个 |
| OLAP | 设置逻辑 | 范围限制 |
|---|---|---|
max_worker_processes | max(100% CPU + 12, 20) | 核数 + 12,保底 20, |
max_parallel_workers | max(ceil(80% CPU, 2)) | 4/5 CPU 上取整,最少两个 |
max_parallel_maintenance_workers | max(ceil(33% CPU), 2) | 1/3 CPU 上取整,最少两个 |
max_parallel_workers_per_gather | max(floor(50% CPU), 2) | 1/2 CPU 上取整,最少两个 |
| CRIT | 设置逻辑 | 范围限制 |
|---|---|---|
max_worker_processes | max(100% CPU + 8, 16) | 核数 + 8,保底 16, |
max_parallel_workers | max(ceil(50% CPU), 2) | 1/2 CPU 上取整,最少两个 |
max_parallel_maintenance_workers | max(ceil(33% CPU), 2) | 1/3 CPU 上取整,最少两个 |
max_parallel_workers_per_gather | 0, 按需启用 |
| TINY | 设置逻辑 | 范围限制 |
|---|---|---|
max_worker_processes | max(100% CPU + 4, 12) | 核数 + 4,保底 12, |
max_parallel_workers | max(ceil(50% CPU) 1) | 50% CPU 下取整,最少1个 |
max_parallel_maintenance_workers | max(ceil(33% CPU), 1) | 33% CPU 下取整,最少1个 |
max_parallel_workers_per_gather | 0, 按需启用 |
请注意,CRIT 和 TINY 模板直接通过设置 max_parallel_workers_per_gather = 0 关闭了并行查询。
用户可以按需在需要时设置此参数以启用并行查询。
OLTP 和 CRIT 模板都额外设置了以下参数,将并行查询的 Cost x 2,以降低使用并行查询的倾向。
parallel_setup_cost: 2000 # double from 100 to increase parallel cost
parallel_tuple_cost: 0.2 # double from 0.1 to increase parallel cost
min_parallel_table_scan_size: 16MB # double from 8MB to increase parallel cost
min_parallel_index_scan_size: 1024 # double from 512 to increase parallel cost
请注意 max_worker_processes 参数的调整必须在重启后才能生效。此外,当从库的本参数配置值高于主库时,从库将无法启动。
此参数必须通过 patroni 配置管理进行调整,该参数由 Patroni 管理,用于确保主从配置一致,避免在故障切换时新从库无法启动。
Pigsty 默认检测 /data/postgres 主数据目录所在磁盘的总空间,并以此作为依据指定下列参数:
{% raw %}
min_wal_size: {{ ([pg_size_twentieth, 200])|min }}GB # 1/20 disk size, max 200GB
max_wal_size: {{ ([pg_size_twentieth * 4, 2000])|min }}GB # 2/10 disk size, max 2000GB
max_slot_wal_keep_size: {{ ([pg_size_twentieth * 6, 3000])|min }}GB # 3/10 disk size, max 3000GB
temp_file_limit: {{ ([pg_size_twentieth, 200])|min }}GB # 1/20 of disk size, max 200GB
{% endraw %}
temp_file_limit 默认为磁盘空间的 5%,封顶不超过 200GB。min_wal_size 默认为磁盘空间的 5%,封顶不超过 200GB。max_wal_size 默认为磁盘空间的 20%,封顶不超过 2TB。max_slot_wal_keep_size 默认为磁盘空间的 30%,封顶不超过 3TB。作为特例, OLAP 模板允许 20% 的 temp_file_limit ,封顶不超过 2TB
除了使用 Pigsty 自动配置的参数外,您还可以手工调整 PostgreSQL 参数。
使用 pg edit-config <cluster> 命令可以交互式编辑集群配置:
pg edit-config pg-meta
或者使用 -p 参数直接设置参数:
pg edit-config -p log_min_duration_statement=1000 pg-meta
pg edit-config --force -p shared_preload_libraries='timescaledb, pg_cron, pg_stat_statements, auto_explain' pg-meta
您也可以使用 Patroni REST API 来修改配置:
curl -u 'postgres:Patroni.API' \
-d '{"postgresql":{"parameters": {"log_min_duration_statement":200}}}' \
-s -X PATCH http://10.10.10.10:8008/config | jq .
本文档列举了 PostgreSQL 和 Pigsty 中可能出现的故障,以及定位、处理、分析问题的 SOP。
磁盘空间写满是最常见的故障类型。
当数据库所在磁盘空间耗尽时,PostgreSQL 将无法正常工作,可能出现以下现象:数据库日志反复报错"no space left on device"(磁盘空间不足), 新数据无法写入,甚至 PostgreSQL 可能触发 PANIC 强制关闭。
Pigsty 带有 NodeFsSpaceFull 告警规则,当文件系统可用空间不足 10% 时触发告警。 使用监控系统 NODE Instance 面板查阅 FS 指标面板定位问题。
您也可以登录数据库节点,使用 df -h 查看各挂载盘符使用率,确定哪个分区被写满。
对于数据库节点,重点检查以下目录及其大小,以判断是哪个类别的文件占满了空间:
/pg/data/base):存放表和索引的数据文件,大量写入与临时文件需要关注pg/data/pg_wal):存放 PG WAL,WAL 堆积/复制槽保留是常见的磁盘写满原因。pg/log):如果 PG 日志未及时轮转写大量报错写入,也可能占用大量空间。data/backups):使用 pgBackRest 等在本机保存备份时,也有可能撑满磁盘。如果问题出在 Pigsty 管理节点或监控节点,还需考虑:
明确占用空间最大的目录后,可进一步使用 du -sh <目录> 深入查找特定大型文件或子目录。
磁盘写满属于紧急问题,需立即采取措施释放空间并保证数据库继续运行。
当数据盘并未与系统盘区分时,写满磁盘可能导致 Shell 命令无法执行。这种情况下,可以删除 /pg/dummy 占位文件,释放少量应急空间以便 shell 命令恢复正常。
如果数据库由于 pg_wal 写满已经宕机,清理空间后需要重启数据库服务并仔细检查数据完整性。
PostgreSQL 循环使用 32 位事务ID (XID),耗尽时会出现"事务号回卷"故障(XID Wraparound)。
第一阶段的典型征兆是 PGSQL Persist - Age Usage 面板年龄饱和度进入警告区域。
数据库日志开始出现:WARNING: database "postgres" must be vacuumed within xxxxxxxx transactions 字样的信息。
若问题持续恶化,PostgreSQL 会进入保护模式:当剩余事务ID不到约100万时数据库切换为只读模式;达到上限约21亿(2^31)时则拒绝任何新事务并迫使服务器停机以避免数据错误。
PostgreSQL 与 Pigsty 默认启用自动垃圾回收(AutoVacuum),因此此类故障出现通常有更深层次的根因。 常见的原因包括:超长事务(SAGE),Autovacuum 配置失当,复制槽阻塞,资源不足,存储引擎/扩展BUG,磁盘坏块。
首先定位年龄最大的数据库,然后可通过 Pigsty PGCAT Database - Tables 面板来确认表的年龄分布。 同时查阅数据库错误日志,通常可以找到定位根因的线索。
relfrozenxid 最大的表运行 VACUUM FREEZE 表名;,优先冻结那些XID年龄最大的表元组。这样可以迅速回收大量事务ID空间。VACUUM FREEZE database_name; 对整个数据库进行冻结清理。完成后再以多用户模式重启数据库。这样做可以解除回卷锁定,让数据库重新可写。需要注意在单用户模式下操作要非常谨慎,并确保有足够的事务ID余量完成冻结。PostgreSQL 有一个最大连接数配置 (max_connections),当客户端连接数超过此上限时,新的连接请求将被拒绝。典型现象是在应用端看到数据库无法连接,并报出类似
FATAL: remaining connection slots are reserved for non-replication superuser connections 或 too many clients already 的错误。
这表示普通连接数已用完,仅剩下保留给超管或复制的槽位
连接耗尽通常由客户端大量并发请求引起。您可以通过 PGCAT Instance / PGCAT Database / PGCAT Locks 直接查阅数据库当前的活跃会话。 并判断是什么样的查询填满了系统,并进行进一步的处理。特别需要关注是否存在大量 Idle in Transaction 状态的连接以及长时间运行的事务(以及慢查询)。
杀查询:对于已经耗尽导致业务受阻的情况,通常立即使用 pg_terminate_backend(pid) 进行紧急降压。
对于使用连接池的情况,则可以调整连接池大小参数,并执行 reload 重载的方式减少数据库层面的连接数量。
您也可以修改 max_connections 参数为更大的值,但本参数需要重启数据库后才能生效。
etcd 配额写满将导致 PG 高可用控制面失效,无法进行配置变更。
Pigsty 在实现高可用时使用 etcd 作为分布式配置存储(DCS),etcd 自身有一个存储配额(默认约为2GB)。 当 etcd 存储用量达到配额上限时,etcd 将拒绝写入操作,报错 “etcdserver: mvcc: database space exceeded"。在这种情况下,Patroni 无法向 etcd 写入心跳或更新配置,从而导致集群管理功能失效。
在 Pigsty v2.0.0 - v2.5.1 之间的版本默认受此问题影响。Pigsty v2.6.0 为部署的 etcd 新增了自动压实的配置项,如果您仅将其用于 PG 高可用租约,则常规用例下不会再有此问题。
目前,TimescaleDB 的试验性存储引擎 Hypercore 被证实存在缺陷,已经出现 VACUUM 无法回收出现 XID 回卷故障的案例。 请使用该功能的用户及时迁移至 PostgreSQL 原生表或者 TimescaleDB 默认引擎
详细介绍:《PG新存储引擎故障案例》
如果是小批量 DELETE 误操作,可以考虑使用 pg_surgery 或者 pg_dirtyread 扩展进行原地手术恢复。
-- 立即关闭此表上的 Auto Vacuum 并中止 Auto Vacuum 本表的 worker 进程
ALTER TABLE public.some_table SET (autovacuum_enabled = off, toast.autovacuum_enabled = off);
CREATE EXTENSION pg_dirtyread;
SELECT * FROM pg_dirtyread('tablename') AS t(col1 type1, col2 type2, ...);
如果被删除的数据已经被 VACUUM 回收,那么使用通用的误删处理流程。
当出现 DROP/DELETE 类误操作,通常按照以下流程决定恢复方案。
如果出现整个数据库集群通过 Pigsty 管理命令被误删的情况,例如错误的执行 pgsql-rm.yml 剧本或 bin/pgsql-rm 命令。
除非您指定了 pg_rm_backup 参数为 false,否则备份会与数据库集群一起被删除。
警告:在这种情况,您的数据将无法找回!请务必三思而后行!
建议:对于生产环境,您可以在配置清单中全局配置此参数为 false,在移除集群时保留备份。
PostgreSQL 已经可以通过物理从库,逻辑从库的方式复制数据, 但有时候你可能需要高速 克隆 一个数据库,数据库实例,或者整个数据库集群。 克隆出来的数据库可以写入,独立演进,不影响原有数据库。在 Pigsty 中,有以下几种克隆方法:
你可以通过 template 机制复制一个 PostgreSQL 数据库,但在此期间不允许有任何连接到模版数据库的活动连接。
假设你想要克隆 postgres 数据库,那么必须一次性同时执行下面两条语句。
确保清理掉所有连接到 postgres 数据库的连接后执行 Clone
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'postgres';
CREATE DATABASE pgcopy TEMPLATE postgres STRATEGY FILE_COPY;
如果你使用的是 PostgreSQL 18 以上的版本,Pigsty 默认为您设置了 file_copy_method。
该参数允许你以 O(1) (约 200ms)的时间复杂度克隆一个数据库,而不需要复制数据文件。
但是您必须显式使用 FILE_COPY 策略来创建数据库。
CREATE DATABASE 的 STRATEGY 参数自 PostgreSQL 15 引入以来的默认值为 WAL_LOG,你需要显式指定 FILE_COPY 来进行瞬间克隆。
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'postgres';
CREATE DATABASE pgcopy TEMPLATE postgres STRATEGY FILE_COPY;
例如,克隆一个 30 GB 的数据库,普通克隆(WAL_LOG)用时 18 秒,而瞬间克隆(FILE_COPY)仅需常数时间 200 毫秒。
但是,您仍然需要确保在克隆期间没有任何连接到模版数据库的活动连接,但这个时间可以非常短暂,因此具有生产环境的实用性。 如果您需要一个新的数据库副本用于测试或开发,瞬间克隆是一个非常好的选择。它并不会引入额外的存储开销,因为它使用了文件系统的 CoW(Copy on Write)机制。
Pigsty v4.0 起,您可以在 pg_databases 参数中使用 strategy: FILE_COPY 来实现瞬间克隆数据库。
pg-meta:
hosts:
10.10.10.10: { pg_seq: 1, pg_role: primary }
vars:
pg_cluster: pg-meta
pg_version: 18
pg_databases:
- name: meta
- name: meta_dev
template: meta
strategy: FILE_COPY # <---- PG 15 引入, PG18 瞬间生效
#comment: "meta clone" # <---- 数据库注释
#pgbouncer: false # <---- 不加入 连接池?
#register_datasource: false # <---- 不加入 Grafana 数据源?
配置完毕后,使用标准数据库创建 SOP 创建该数据库即可:
bin/pgsql-db pg-meta meta_dev
请注意,这个特性仅在支持的文件系统上可用(xfs,brtfs,zfs,apfs),如果文件系统不支持,PostgreSQL 将会报错失败。 默认情况下,主流操作系统发行版的 xfs 都已经默认启用 reflink=1 选项,因此大多数情况下您不需要担心这个问题。 OpenZFS 需要显式配置才能支持 CoW,但因为存在数据损坏的先例,不建议将此特性用于生产。
如果您使用的 PostgreSQL 版本低于 15,指定 strategy 不会有任何效果。
请不要使用 postgres 数据库作为模版数据库进行克隆,因为管理链接通常会连接到 postgres 数据库,这阻止了克隆操作的进行。
如果您确实需要克隆 postgres 数据库,请你手动连接到其他数据库上后,自行执行 SQL 实现。
在极高并发/吞吐的生产环境中使用瞬间克隆需要谨慎,它需要在克隆窗口(200ms)内清理掉所有连接到模版数据库的连接,否则克隆会失败。
要确保 Pigsty 与 PostgreSQL 集群健康稳定运行,需要进行一些例行维护保养工作。
Pigsty 提供了开箱即用的监控平台,我们建议您每天浏览一次监控大盘,关注系统状态。 极端情况下,我们建议您每周至少查阅一次监控,关注出现的告警事件,这样可以提前规避绝大多数故障与问题。
这里列举了 Pigsty 中预先定义的 告警规则 列表。
Pigsty 的高可用架构允许 PostgreSQL 集群自动进行主从切换,这意味着运维与 DBA 无需即时介入与响应。 然而用户仍然需要在合适的时机(例如第二天工作日)进行以下善后工作,包括:
bin/pgsql-svc 刷新负载均衡器配置,更新服务的路由状态bin/pgsql-hba 刷新集群的 HBA 规则,避免主从特定的规则漂移bin/pgsql-rm 移除故障服务器,并通过 bin/pgsql-add 扩容一台新从库长时间运行的 PostgreSQL 会出现 “表膨胀” / “索引膨胀” 现象, 导致系统性能劣化。
定期使用 pg_repack 对表与索引进行在线重建,有助于维护 PostgreSQL 的良好性能表现。
Pigsty 已经默认在所有数据库中安装并启用了此扩展,因此您可以直接使用。
您可以通过 Pigsty 的 PGCAT Database - Table Bloat 面板,
确认数据库中的表膨胀情况与索引膨胀情况。并选择膨胀率较高(膨胀率高于 50% 的较大表)的表与索引,使用 pg_repack 进行在线重整:
pg_repack dbname -t schema.table
重整期间不会影响正常读写,但重整完毕之后的 切换瞬间 需要获取表上的 AccessExclusive 锁阻塞一切访问。 因此对于高吞吐量业务,建议在业务低峰期或者维护窗口进行。更多细节,请参考:关系膨胀的治理
冻结过期事务ID(VACUUM FREEZE)是PostgreSQL重要的维护任务,用于防止事务ID (XID) 用尽导致停机。 尽管 PostgreSQL 已经提供了自动垃圾回收(AutoVacuum)机制,然而对于高标准的生产环境, 我们依然建议结合自动和手动两种方式,定期执行全库级别的 VACUUM FREEZE ,以确保 XID 安全。
您可以使用以下命令手工对数据库执行 VACUUM FREEZE:
-- 对整个数据库执行 VACUUM FREEZE
VACUUM FREEZE;
-- 对特定表执行 VACUUM FREEZE
VACUUM FREEZE schema.table_name;
或者通过 crontab 设置定时任务,例如每周日凌晨执行:
# 每周日凌晨3点对所有数据库执行 VACUUM FREEZE
0 3 * * 0 postgres psql -c 'VACUUM FREEZE;' dbname
要执行小版本的服务器升级/降级,您首先需要在本地软件仓库中 添加软件:最新的PG小版本 RPM/DEB。
首先对所有从库执行滚动升级/降级,然后执行集群 主从切换以升级/降级主库。
ansible <cls> -b -a "yum upgrade/downgrade -y <pkg>" # 升级/降级软件包
pg restart --force <cls> # 重启集群
这次我们采用滚动方式升级:
ansible pg-test -b -a "yum upgrade -y postgresql15*" # 升级软件包(或 apt upgrade)
ansible pg-test -b -a '/usr/pgsql/bin/pg_ctl --version' # 检查二进制版本是否为15.2
pg restart --role replica --force pg-test # 重启从库
pg switchover --leader pg-test-1 --candidate=pg-test-2 --scheduled=now --force pg-test # 切换主从
pg restart --role primary --force pg-test # 重启主库
将15.1的包添加到软件仓库并刷新节点的 yum/apt 缓存:
cd ~/pigsty; ./infra.yml -t repo_upstream # 添加上游仓库
cd /www/pigsty; repotrack postgresql15-*-15.1 # 将15.1的包添加到yum仓库
cd ~/pigsty; ./infra.yml -t repo_create # 重建仓库元数据
ansible pg-test -b -a 'yum clean all' # 清理节点仓库缓存
ansible pg-test -b -a 'yum makecache' # 从新仓库重新生成yum缓存
# 对于 Ubutnu/Debian 用户,使用 apt 替换 yum
ansible pg-test -b -a 'apt clean' # 清理节点仓库缓存
ansible pg-test -b -a 'apt update' # 从新仓库重新生成apt缓存
执行降级并重启集群:
ansible pg-test -b -a "yum downgrade -y postgresql15*" # 降级软件包)
pg restart --force pg-test # 重启整个集群以完成升级
实现大版本升级的最简单办法是:创建一个使用新版本的新集群,然后通过逻辑复制,蓝绿部署,并进行 在线迁移。
您也可以进行原地大版本升级,当您只使用数据库内核本身时,这并不复杂,使用 PostgreSQL 自带的 pg_upgrade 即可:
假设您想将 PostgreSQL 大版本从 14 升级到 15,您首先需要在仓库中 添加软件,并确保两个大版本两侧安装的核心扩展插件也具有相同的版本号。
./pgsql.yml -t pg_pkg -e pg_version=15 # 安装pg 15的包
sudo su - postgres; mkdir -p /data/postgres/pg-meta-15/data/ # 为15准备目录
pg_upgrade -b /usr/pgsql-14/bin/ -B /usr/pgsql-15/bin/ -d /data/postgres/pg-meta-14/data/ -D /data/postgres/pg-meta-15/data/ -v -c # 预检
pg_upgrade -b /usr/pgsql-14/bin/ -B /usr/pgsql-15/bin/ -d /data/postgres/pg-meta-14/data/ -D /data/postgres/pg-meta-15/data/ --link -j8 -v -c
rm -rf /usr/pgsql; ln -s /usr/pgsql-15 /usr/pgsql; # 修复二进制链接
mv /data/postgres/pg-meta-14 /data/postgres/pg-meta-15 # 重命名数据目录
rm -rf /pg; ln -s /data/postgres/pg-meta-15 /pg # 修复数据目录链接