克隆副本
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)内清理掉所有连接到模版数据库的连接,否则克隆会失败。
克隆实例
有时候,您想要用现有的 PostgreSQL 实例在 同一台机器 上创建一个新的实例 (用于测试,PITR 恢复),可以使用 postgres 用户执行下面的命令:
psql <<EOF
CHECKPOINT;
SELECT pg_backup_start('pgfork', true);
\! rm -rf /pg/data2 && cp -r --reflink=auto /pg/data /pg/data2 && ls -alhd /pg/data2
SELECT * FROM pg_backup_stop(false);
EOF
# 修改配置,避免与现有实例冲突:端口,日志,归档等
sed -i 's/^port.*/port = 5431/' /pg/data2/postgresql.conf;
sed -i 's/^log_destination.*/log_destination = stderr/' /pg/data2/postgresql.conf;
sed -i 's/^archive_mode.*/archive_mode = off/' /pg/data2/postgresql.conf;
rm -rf /pg/data2/postmaster.pid /pg/data2/postmaster.opts
pg_ctl -D /pg/data2 start -l /pg/log/pgfork.log
pg_ctl -D /pg/data2 stop
psql -p 5431 # 访问新实例
上面的命令会创建一个新的数据目录 /pg/data2,它是现有数据目录 /pg/data 的一个完整拷贝。
如果您使用的是 XFS (启用了 reflink COW 特性),那么同磁盘拷贝目录会非常快,通常几百毫秒的常数时间内即可完成。
您在原地拉起新实例前,务必 修改 postgresql.conf 里的 port / archive_mode / log_destination 参数,避免影响现有生产实例等运行。
您可以使用一个没有被占用的端口,例如 5431,并将日志输出到 /pg/log/xxxx.log 避免写脏现有实例的日志文件。
我们建议同时修改 shared_buffers Pigsty 默认情况通常分配 25% 的系统内存给 PostgreSQL 实例,
开启新实例时,会与现有实例争夺内存资源。您可以适当调小,以减小对现有生产实例的影响。
克隆集群
您可以使用 PG PITR 机制克隆整个数据库集群。
详情参考 备份-恢复集群