克隆副本

如何克隆数据库,克隆数据库实例,克隆数据库集群?

PostgreSQL 已经可以通过物理从库,逻辑从库的方式复制数据, 但有时候你可能需要高速 克隆 一个数据库,数据库实例,或者整个数据库集群。 克隆出来的数据库可以写入,独立演进,不影响原有数据库。在 Pigsty 中,有以下几种克隆方法:

  • 克隆数据库:在同一个集群内,克隆一个新的数据库
  • 克隆实例:在同一个 PG 节点上,克隆一个新的实例
  • 克隆集群:使用 PITR 机制创建一个新的数据库集群,并恢复到指定集群的任意时间点

克隆数据库

你可以通过 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 DATABASESTRATEGY 参数自 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 机制克隆整个数据库集群。

详情参考 备份-恢复集群