pg_repack 更适合高写入、大索引的 oltp 场景,支持主表、索引及 toast 并行重建,不依赖逻辑复制;pg_squeeze 基于逻辑复制,要求 wal_level=logical 且必须有主键或非空唯一约束,不重建索引、对复制延迟敏感、安装配置更复杂。

pg_repack 和 pg_squeeze 都能在线重整形表,但 pg_squeeze 不支持索引重建
pg_repack 是成熟、稳定、功能完整的在线表重整工具,支持主表 + 所有索引 + TOAST 表的并行重建;pg_squeeze 本质是基于逻辑复制(pg_logical)的“替换式”方案,它会新建表和索引再原子切换,但当前版本(v1.0–v1.2)不重建索引——旧索引直接 DROP 后用新表上的 CREATE INDEX,无法复用原索引的排序或部分构建状态。
这意味着:如果表带大索引(比如几百 GB 的 GIN 或 BRIN),pg_squeeze 会多出一次完整索引构建耗时,而 pg_repack 可以保留原索引结构并增量整理。实操中,对高写入+大索引的 OLTP 表,pg_repack 更可控。
- pg_repack 默认启用
--no-order时按物理顺序重建,适合减少后续 I/O;加--order-by可指定排序字段,但会显著拖慢执行时间 - pg_squeeze 要求源表必须有主键或非空唯一约束(用于逻辑复制标识行),否则启动就报错:
ERROR: table "xxx" has no primary key or non-nullable unique constraint - pg_squeeze 的切换阶段会短暂持有
ACCESS EXCLUSIVE锁(毫秒级),但 pg_repack 在最后切换时也需同等级别锁——两者锁行为差异不大,别信“pg_squeeze 完全无锁”的说法
pg_squeeze 对 WAL 和复制延迟更敏感,容易在备库上卡住
因为 pg_squeeze 依赖 pg_logical 订阅消费变更,所有 DML 都要走逻辑解码 → 复制槽 → 应用到新表。一旦备库落后、复制槽被阻塞、或 max_replication_slots 不足,pg_squeeze 就会 hang 在 waiting for logical replication to catch up 状态,且不会自动超时退出。
pg_repack 不依赖逻辑复制,只靠触发器捕获变更,WAL 压力小,也不受复制槽状态影响。在主从架构复杂、或使用 wal_level = replica(而非 logical)的环境里,pg_squeeze 直接不可用。
- 启用 pg_squeeze 前必须确认:
wal_level = logical、max_replication_slots >= 2(一个给 squeeze 自用,一个防其他业务占用)、shared_preload_libraries包含pg_squeeze - pg_repack 的触发器对写入吞吐有可测影响(约 5–15%),但属于恒定开销;pg_squeeze 的逻辑解码压力随变更量陡增,高峰期可能打满 CPU 或拖慢主库解析速度
- pg_squeeze 的日志里频繁出现
logical decoding output plugin "pg_squeeze_output" produced invalid output,通常是插件版本与 PostgreSQL 小版本不匹配(如 pg_squeeze v1.1 在 PG 15.5 上需手动 patch)
pg_repack 的安装和权限模型更简单,pg_squeeze 需要更多 DBA 协调
pg_repack 只需要超级用户或具备 EXECUTE 权限的普通用户(配合 pg_repack 扩展已安装),建扩展后就能跑 pg_repack 命令;pg_squeeze 要求更高:除扩展安装外,还必须由超级用户创建专用复制槽、设置 pg_squeeze 用户角色、并在 pg_hba.conf 中允许该用户连接本地逻辑复制端口(默认 5432)。
更麻烦的是,pg_squeeze 进程本身是外部 Python 脚本(pg_squeeze.py),不是数据库内函数,它要连两次数据库(一次查元数据,一次建订阅),网络、认证、SSL 配置都得单独处理。稍有疏漏就报:could not connect to server: Connection refused 或 FATAL: database "postgres" does not exist(其实是连到了错误的数据库名)。
- pg_repack 的二进制包(如 Debian 的
postgresql-15-repack)自带pg_repackCLI,路径通常在/usr/lib/postgresql/15/bin/pg_repack - pg_squeeze 必须用
pip install pg-squeeze安装,且 Python 版本需与服务器上pg_config --bindir找到的pg_config兼容,否则pg_squeeze.py --version会直接段错误 - 运行 pg_squeeze 时若忘记加
--dry-run,它会在后台默默启一堆pg_squeeze_worker进程,杀不干净会导致复制槽残留,最终撑爆pg_wal/
不要只看文档说的“零停机”,得看你的表有没有长事务和未提交的 prepared statement
两个工具都会在启动前检查活跃事务,但 pg_repack 检查的是 pg_stat_activity 中 state = 'active' 的连接,pg_squeeze 还额外检查 pg_prepared_xacts。如果你有遗留的 prepared transaction(比如应用崩溃没清理),pg_squeeze 会直接拒绝启动,并提示:prepared transaction found, abort it first with ROLLBACK PREPARED 'xxx';pg_repack 则可能跳过检查、后续在切换阶段失败。
真正棘手的是那些持锁超过 30 秒的慢查询——它们不会被工具自动 kill,反而会让 pg_repack 的触发器堆积变更、让 pg_squeeze 的逻辑复制 lag 持续增长。这种情况下,与其硬扛,不如先 SELECT pid, query, now() - backend_start FROM pg_stat_activity WHERE state = 'active' AND now() - backend_start > interval '30 seconds'; 手动干预。
- pg_repack 的
--wait-timeout参数控制等待锁释放的上限(默认 60 秒),超时后报错退出,不自动重试 - pg_squeeze 没有等锁机制,它依赖逻辑复制“追平”,所以实际停机窗口 = 最长事务运行时间 + 复制追赶时间,这个值可能远超预期
- 任何在线重整,只要表上有
BEFORE ROW触发器(尤其是含INSERT/UPDATE/DELETE的),都建议先禁用——pg_repack 和 pg_squeeze 都无法保证与这类触发器的行为兼容











