PostgreSQL 19: REPACK 命令支持 CONCURRENTLY 选项

John Doe 四月 9, 2026

PostgreSQL 原生REPACK命令新增 CONCURRENTLY(并发执行)选项,实现了业务表几乎无感知的 7x24 小时在线重组。

image

目录

特性提交日志

为 REPACK 命令添加 CONCURRENTLY 选项。

当指定该选项时,REPACK 在创建表的新副本期间不再获取访问排他锁(access-exclusive lock);取而代之的是,它仅在共享更新排他锁(share-update-exclusive lock) 下创建初始副本(与 VACUUM 等命令锁级别相同),并基于 MVCC 快照进行操作。它会从该快照开始创建一个复制槽,并使用一个并发后台工作进程,从该快照起点执行逻辑解码,将并发产生的数据变更暂存起来。

在交换表物理文件(relfilenode)之前,这些累积的变更会被重新应用到表的新副本上。

应用程序可以继续正常访问表的原始副本,直到交换操作开始前一刻 —— 只有在这个时间点才需要获取访问排他锁。

本次提交中仍存在一些尚未完善的问题:

  1. 并发 REPACK 需要专属的复制槽以执行逻辑解码,而复制槽属于稀缺资源,容易耗尽。
  2. 受历史快照初始创建方式的限制,整个系统同一时间只能运行一个 REPACK 进程。
  3. 在最后阶段需要进行锁升级,存在死锁风险(进而导致任务中止)。

这些问题将在后续提交中逐步解决。

本特性的设计及大部分代码由 Antonin Houska 完成,主要基于其开发的第三方扩展 pg_squeeze 实现。

讨论:https://postgr.es/m/5186.1706694913@antos

表重组命令的痛点

PostgreSQL 的原生CLUSTER命令,用于完成表碎片回收、按索引重排数据、释放空闲磁盘空间等核心运维操作,是解决表膨胀、提升范围查询性能的关键工具。

但传统CLUSTER存在致命缺陷:全程需要对目标表施加 ACCESS EXCLUSIVE 锁 —— 这是 PostgreSQL 最高级别的表锁,会阻塞所有对该表的读、写操作。对于几百 GB 甚至 TB 级的核心业务表,重组过程可能持续数小时,意味着业务必须完全中断,仅能在凌晨低峰维护窗口执行,甚至很多 7x24 小时运行的业务根本不敢使用,最终导致表膨胀持续加剧、查询性能持续下降。

此前社区只能依赖pg_repackpg_squeeze等第三方插件实现并发重组,而本次为 REPACK 命令添加的CONCURRENTLY选项,将无锁重组能力原生内置到 PostgreSQL 内核,无需额外安装插件,稳定性、兼容性、安全性大幅提升。

特性示例

例如有个电商平台核心订单表,数据量 500GB,碎片率超过 35%,查询性能持续下降,业务 7x24 小时运行,无长维护窗口。

使用CONCURRENTLY模式在线重组,业务完全无感知。在线重组订单表,回收碎片空间,自动更新统计信息:

REPACK (CONCURRENTLY true, ANALYZE true) TABLE public.orders;

全程不阻塞订单的创建、查询、支付等核心操作,仅在最终切换时有毫秒级锁等待,碎片率降至 1% 以内,查询性能恢复至最优水平。

例如,我们对一个日志表清理了 3 年前的历史数据,释放了 200GB 逻辑空间,但 PostgreSQL 未将空间返还给操作系统,磁盘占用率居高不下。

并发重组回收空闲空间,无需中断日志写入。

-- 清理历史数据
DELETE FROM public.logs WHERE report_time < '2023-01-01';
-- 并发重组表,将空闲空间返还给操作系统
REPACK (CONCURRENTLY true) TABLE public.logs;

非常不错的体验,感谢所有参与的社区人员,PostgreSQL 19 值得期待。

使用限制与注意事项

CONCURRENTLY模式存在明确的使用边界,以下场景无法使用:

  1. 不支持 UNLOGGED 表、分区表、系统目录表、TOAST 表;
  2. 目标表必须拥有主键,或设置了REPLICA IDENTITY FULL(逻辑解码依赖行唯一标识);
  3. 不能在事务块中执行;
  4. 不支持全库级别的 REPACK,仅支持单表执行;
  5. 必须保证max_replication_slots配置有剩余配额,用于创建逻辑复制槽;
  6. 无法对执行过程中的表结构变更(DDL)做兼容,并发 DDL 会导致 REPACK 任务失败。

另外,与 TRUNCATE、表重写型 ALTER TABLE 一致,该操作非 MVCC 安全,持有操作前快照的长事务,可能在操作完成后看到表数据不一致。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/28d534e2ae0ac888b5460f977a10cd9bb017ef98