首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Every Day of a DBA,第151期: PostgreSQL 18 预览:file_copy_method = CLONE,数据库终于有了"写时复制"快照

Every Day of a DBA,第151期: PostgreSQL 18 预览:file_copy_method = CLONE,数据库终于有了"写时复制"快照

作者头像
用户3107127
发布2026-07-09 16:09:14
发布2026-07-09 16:09:14
120
举报

还在用 CREATE DATABASE 拷半天数据?PG 18 让你秒建一个完整的库副本,零额外磁盘。


01. 一个DBA 的痛

如果你管过 PostgreSQL 数据库集群,下面这个场景你一定不陌生:

「给我搭一套测试环境,要跟线上一样的数据量。」

你的第一反应是什么?

—— pg_dump 导出一小时,pg_restore 导入又一小时。一个 200GB 的库,光拷贝就耗掉半天。等环境就绪,开发已经自己拿 navicat 连上生产库了……

这是 DBA 的日常,也是数据库分支管理至今最大的痛点。

而 PostgreSQL 18 给了我们一个全新的答案:CLONE


02. 什么是 file_copy_method?

PG 18 新增了一个 GUC 参数:

代码语言:javascript
复制
file_copy_method = clone | copy

默认值是 copy——老老实实逐字节拷贝。而 clone 则使用操作系统的 Copy-on-Write(写时复制) 机制,底层调用的系统调用是:

  • Linux / FreeBSD → copy_file_range()
  • macOS → copyfile()

写时复制是什么意思?打个比方:

你有一本厚书,同事说「借我看看」。COPY 模式:你花半小时复印一整本给他。CLONE 模式:你把书往桌上一放,「看吧,咱俩一起看。你要划线写字的地方,我再给你撕页重印。」

共享物理块,修改时才复制。 这就是 CLONE 的精髓。


03. 先搭环境

为了让下面的实验可复现,先用 IvorySQL 在线试用环境(trial.ivorysql.org)准备基础设施。

3.1 创建目录与表空间

代码语言:javascript
复制
# 高速业务存储
mkdir-p /opt/tbs_fast && chown postgres:postgres /opt/tbs_fast

# 归档历史存储
mkdir-p /opt/tbs_archive && chown postgres:postgres /opt/tbs_archive
代码语言:javascript
复制
-- 高速表空间
CREATETABLESPACE tbs_fast LOCATION '/opt/tbs_fast';

-- 归档表空间
CREATETABLESPACE tbs_archive LOCATION '/opt/tbs_archive';

GRANTCREATEONTABLESPACE tbs_fast, tbs_archive TO postgres;

3.2 主干模板库

代码语言:javascript
复制
-- 主干库,默认 pg_default 表空间
CREATEDATABASE db_template_main;

\c db_template_main

-- 业务表(默认表空间,会随库迁移)
CREATETABLE biz_base(id bigintprimarykey, info text);

-- 字典表(固定在 tbs_archive,不受整库迁移影响)
CREATETABLE dict_static(codeint, name text)TABLESPACE tbs_archive;

-- 灌入 1 万行基础数据
INSERTINTO biz_base SELECT generate_series(1,10000),'low data';

04. 场景 1:整库迁移表空间(切换存储介质)

假如你要把整个库从 HDD 迁到 SSD,传统的做法是什么?

数据量小还好,几 TB 的库意味着好几小时的 I/O 压力。

4.1 切断所有活跃连接

代码语言:javascript
复制
-- 强制终止
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname ='db_template_main';

4.2 CLONE 模式迁移(推荐)

代码语言:javascript
复制
-- 当前会话启用 COW 克隆
SET file_copy_method ='clone';

-- 极速迁移至 SSD 表空间
ALTERDATABASE db_template_main SETTABLESPACE tbs_fast;

注意:文件系统需支持 reflink(XFS、Btrfs、ZFS 表现最佳),ext4 可能降级为 COPY。

4.3 对比:COPY 模式(兼容所有 FS)

代码语言:javascript
复制
SET file_copy_method ='copy';
ALTERDATABASE db_template_main SETTABLESPACE tbs_fast;

4.4 迁移效果

  • biz_base:物理文件从 pg_default 移动到 tbs_fast
  • dict_static:绑定在 tbs_archive纹丝不动(手动指定表空间不受整库迁移影响)
  • 后续新表默认存储 → tbs_fast

4.5 验证

代码语言:javascript
复制
-- 1. 确认默认表空间切换
SELECT datname, spcname AS db_default_tablespace
FROM pg_database d
JOIN pg_tablespace ts ON d.dattablespace= ts.oid
WHERE datname ='db_template_main';

-- 2. 查看表关联表空间
\c db_template_main
SELECT relname, spcname
FROM pg_class c
LEFTJOIN pg_tablespace ts ON c.reltablespace= ts.oid
WHERE relname IN('biz_base','dict_static');

-- 3. 底层物理文件路径(最权威)
SELECT relname, pg_relation_filepath(c.oid)AS file_path
FROM pg_class c
WHERE relname IN('biz_base','dict_static');

05. 场景 2:Git 式创建多版本数据库(核心功能)

这才是 CLONE 真正闪光的场景——像 Git 分支一样管理数据库版本

5.1 CLONE 快照模式(秒级,零额外空间)

代码语言:javascript
复制
SET file_copy_method = 'clone';

-- 创建开发分支
CREATE DATABASE db_v1_test
TEMPLATE db_template_main
STRATEGY = FILE_COPY;

-- 创建预发布分支
CREATE DATABASE db_v2_pre
TEMPLATE db_template_main
STRATEGY = FILE_COPY;

验证:

代码语言:javascript
复制
$ psql -d db_template_main -c "SELECT oid, datname FROM pg_database WHERE datname IN ('db_template_main','db_v1_test');"
  oid  |     datname
-------+------------------
 16386 | db_template_main
 16400 | db_v1_test

# 对比文件列表——完全一致,仅 inode 不同
ls -1 /opt/tbs_fast/PG_18_202506291/16386 | sort > /tmp/tmp.list
ls -1 /opt/tbs_fast/PG_18_202506291/16400 | sort > /tmp/clone.list
diff /tmp/tmp.list /tmp/clone.list   # ← 无输出,文件清单完全相同

目录结构、文件名、文件大小完全相同,只有 inode 不同——这就是 reflink 的特征。

5.2 COPY 完整副本模式(独立隔离)

代码语言:javascript
复制
SET file_copy_method = 'copy';
CREATE DATABASE db_prod_backup
TEMPLATE db_template_main
STRATEGY = FILE_COPY;

每个文件全新拷贝,inode 全部不同,适合生产备份与离线归档。


06. 全局开启 CLONE

6.1 修改 postgresql.conf

代码语言:javascript
复制
# COW 克隆默认开启,系统不支持时自动降级 COPY
file_copy_method = clone

6.2 重载配置

代码语言:javascript
复制
SELECT pg_reload_conf();
SHOW file_copy_method;   -- 确认生效

07. 终极拷问:能实现"数据库分支-合并"吗?

看到这里,你一定会想:

能不能像 Git 一样,创建分支 → 开发 → 合并回主线?

我的回答是:CLONE 提供了分支的「创建能力」,但「合并」要自己搭桥。

✅ CLONE 做得很好的

能力

评价

创建快照分支

✅ 秒级,零额外空间

独立开发/测试

✅ 完全可写,COW 隔离

并行多版本

✅ 磁盘零增长

删除分支

✅ DROP DATABASE

❌ CLONE 做不到的

能力

现状

解决方案

DDL 合并(结构变更)

❌ 无原生支持

Flyway / Liquibase / sqldef

DML 合并(数据变更)

❌ 无原生支持

逻辑复制 / MERGE SQL / pg_dump

冲突检测

❌ 无原生支持

乐观锁(版本号字段)+ 应用层

分支历史

❌ 无原生支持

外部工具记录

一个简单的合并示例

代码语言:javascript
复制
-- 将分支数据合并回主线
MERGE INTO biz_base t
USING dblink('dbname=db_v1_test', 'SELECT id, info FROM biz_base') s(id, info)
ON t.id = s.id
WHEN MATCHED AND t.info IS DISTINCT FROM s.info
  THEN UPDATE SET info = s.info, updated_at = now()
WHEN NOT MATCHED
  THEN INSERT (id, info) VALUES (s.id, s.info);

推荐架构

代码语言:javascript
复制
外层编排工具(pg_branch.py)
     ├── CLONE → 创建快照分支  (PG 18 原生)
     ├── DDL diff + apply      (迁移工具)
     ├── 逻辑复制 / MERGE      (DML 同步)
     └── DROP DATABASE         (清理)

CLONE 是基石,不是全部。 但它补上了数据库分支管理最后一块拼图——让创建分支的成本从「小时级」降到「秒级」。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ByteHouse 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01. 一个DBA 的痛
  • 02. 什么是 file_copy_method?
  • 03. 先搭环境
    • 3.1 创建目录与表空间
    • 3.2 主干模板库
  • 04. 场景 1:整库迁移表空间(切换存储介质)
    • 4.1 切断所有活跃连接
    • 4.2 CLONE 模式迁移(推荐)
    • 4.3 对比:COPY 模式(兼容所有 FS)
    • 4.4 迁移效果
    • 4.5 验证
  • 05. 场景 2:Git 式创建多版本数据库(核心功能)
    • 5.1 CLONE 快照模式(秒级,零额外空间)
    • 5.2 COPY 完整副本模式(独立隔离)
  • 06. 全局开启 CLONE
    • 6.1 修改 postgresql.conf
    • 6.2 重载配置
  • 07. 终极拷问:能实现"数据库分支-合并"吗?
    • ✅ CLONE 做得很好的
    • ❌ CLONE 做不到的
    • 一个简单的合并示例
    • 推荐架构
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档