数据库管理

数据库管理:创建、修改、删除、重建数据库,使用模板克隆数据库

在 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 的业务数据库

  1. 在配置文件中添加数据库定义:
pg-meta:
  vars:
    pg_databases:
      - name: myapp
        owner: dbuser_myapp
        schemas: [app]
        extensions:
          - { name: pg_trgm }
          - { name: btree_gin }
        comment: my application database
  1. 执行创建命令:
bin/pgsql-db pg-meta myapp

执行效果:

  • 在主库上创建数据库 myapp
  • 设置数据库所有者为 dbuser_myapp
  • 创建 schema app
  • 安装扩展 pg_trgmbtree_gin
  • 配置默认权限(dbrole_readonly/readwrite/admin)
  • 将数据库添加到 Pgbouncer 连接池
  • 将数据库注册到 Grafana 数据源

修改数据库

修改数据库属性可以通过更新配置并重新执行剧本来完成:

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_pathSchema 搜索路径'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;

管理扩展

扩展通过 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;

删除数据库

要删除数据库,将其 state 设置为 absent 并执行剧本:

pg_databases:
  - name: olddb
    state: absent
bin/pgsql-db <cls> olddb

删除操作会:

  1. 如果数据库标记为 is_template: true,先执行 ALTER DATABASE ... IS_TEMPLATE false
  2. 使用 DROP DATABASE ... WITH (FORCE) 强制删除数据库(PG13+)
  3. 终止所有到该数据库的活动连接
  4. 从 Pgbouncer 连接池中移除该数据库
  5. 从 Grafana 数据源中取消注册

保护机制:

  • 系统数据库 postgrestemplate0template1 无法删除
  • 删除操作仅在主库上执行,流复制会自动同步到从库

重建数据库

recreate 状态用于重建数据库,等效于先删除再创建:

pg_databases:
  - name: testdb
    state: recreate
    owner: dbuser_test
    baseline: test_init.sql    # 重建后执行初始化
bin/pgsql-db <cls> testdb

适用场景:

  • 测试环境重置
  • 清空开发数据库
  • 修改不可变属性(编码、本地化等)
  • 恢复数据库到初始状态

与手动 DROP + CREATE 的区别:

  • 单条命令完成,无需两次操作
  • 自动保留 Pgbouncer 和 Grafana 配置
  • 执行后自动加载 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

指定克隆策略(PG15+)

- 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 本地化提供者

使用 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 参数,支持不同的本地化实现。

使用 ICU 提供者(PG15+)

- name: myapp_icu
  template: template0        # ICU 必须使用 template0
  locale_provider: icu
  icu_locale: en-US          # ICU 本地化规则
  encoding: UTF8

使用内置提供者(PG17+)

- name: myapp_builtin
  template: template0
  locale_provider: builtin
  builtin_locale: C.UTF-8    # 内置本地化规则
  encoding: UTF8

ICU 排序规则(PG16+)

- name: myapp_custom_icu
  template: template0
  locale_provider: icu
  icu_locale: en-US
  icu_rules: '&V << w <<< W'  # 自定义 ICU 排序规则

提供者对比

提供者版本要求特点
libc-传统方式,依赖操作系统
icuPG15+跨平台一致,功能丰富
builtinPG17+最高效的 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 执行时会依次:

  1. 验证 - 检查 dbname 参数和数据库定义
  2. 删除(如果 state=absent/recreate)- 执行 DROP DATABASE
  3. 创建(如果 state=create/recreate)- 执行 CREATE DATABASE
  4. 配置 - 执行 ALTER DATABASE 设置属性
  5. 初始化 - 创建 schema、安装扩展、执行 baseline
  6. 注册 - 更新 Pgbouncer 和 Grafana 数据源

关于数据库的访问权限,请参考 ACL:数据库权限 一节。


最后修改 2026-01-09: add supabase asciinema demo (3104503)