从 VACUUM FULL 到 REPACK:PostgreSQL 表重写技术的进化史

SpringBoot+Vue+MySQL 高校学科竞赛平台 完整可运行版(源码+文档) 本项目基于SpringBoot+Vue+MySQL构建高校学科竞赛管理平台,覆盖学生、教师、领队教师、管理员四大角色,实现竞赛发布、报名、刷题、评分、公示等全流程闭环管理。系统架构清晰,技术主流,配套完整源码、数据库脚本、万字论文、开题报告、答辩PPT及部署文档,支持一键运行,适用于毕业设计、课程设计及院系二次开发,真正实现“开箱即用”。 阅读详情

本文作者:付超,Oracle ACE 与 PostgreSQL ACE 双认证专家,PG 分会西安用户组核心成员。长期从事数据库运维与技术布道工作,具备 Oracle OCM、Kubernetes CKS 等多项专业认证。

每个 DBA 都经历过这样的场景:一张业务大表因为频繁更新,膨胀到实际数据量的好几倍,查询越来越慢。你决定用 VACUUM FULL 把它"压缩"回正常体积,却发现命令一执行,这张表的读写全部被锁死——业务侧立刻报警,你只能灰溜溜地挑一个凌晨窗口再跑。

这是 PostgreSQL 表维护里最经典的矛盾:重写表需要空间上的"大动干戈",而业务不允许片刻停摆。围绕这个矛盾,社区和内核团队先后给出了三代解决方案。这篇文章就沿着这条演进线,讲讲 VACUUM FULL、pg_repack 和 PG19 的 REPACK 各自的思路、代价与取舍。

一、表为什么要"重写"?

要理解三代方案,先得明白重写(rewrite)和清理(vacuum)是两回事。

PostgreSQL 基于 MVCC 实现多版本并发控制:一条记录被 UPDATE 后,旧版本不会立即删除,而是留在原页面里成为"死元组",等 VACUUM 来回收。问题在于,VACUUM 只是把页面内部的死元组标记为可复用,并不会把空间还给操作系统——页面还在,文件还在,表"瘦"不下来。这就是所谓的"膨胀"(bloat)。

要让表真正瘦身,唯一的办法是重写:把表中还活着的行,原样拷贝到一个全新的物理文件中,然后把旧文件整个丢弃。新文件里没有死元组留下的空洞,磁盘空间自然就还给了操作系统。

重写还有一个附加能力:物理排序。如果把行按某个索引的顺序写入新文件,那么范围查询时,符合条件的数据会集中在相邻页面,磁盘 IO 大幅减少——这就是"聚簇"(CLUSTER)的价值。

所以三代方案其实都在做同一件事:重写表。区别只在于一个核心问题——重写期间,表上的并发变更(插入、更新、删除)怎么处理?

二、第一代:锁住一切——VACUUM FULL 与 CLUSTER

最朴素的做法,也是最古老的做法:重写期间给表加 ACCESS EXCLUSIVE 锁。

VACUUM FULL 把整张表重写到新文件,只回收空间、不排序;CLUSTER 在重写的同时按指定索引排好物理顺序。两者的执行期间,其他事务对这张表的一切读写都被阻塞,直到重写完成。

这套方案的优点是无可挑剔的简单:内核自带、零依赖、没有任何前置条件,数据安全上也是最保守的。对于中小表、或者能接受维护窗口的系统,它至今依然是可靠的选择——PG19 也继续保留这两个命令的兼容。实际用起来也很直白:

-- 回收表空间(不排序),可指定单表或整库
VACUUM FULL orders;
VACUUM FULL;                          -- 当前库所有表

-- 按索引物理排序(聚簇)
CLUSTER orders USING orders_pkey;

-- 聚簇索引管理:先配置默认聚簇索引,再按它执行,最后刷新统计信息
ALTER TABLE orders CLUSTER ON orders_pkey;
CLUSTER orders;
ANALYZE orders;                       -- 重写后必须刷新统计信息

但它的天花板也很明显:大表上不可行。一张 TB 级的表重写可能要几十分钟甚至更久,期间业务完全停摆。在"7×24 小时在线"成为标配的今天,这几乎等于宣判了第一代方案在大表场景下的死刑。社区因此开始思考:能不能让重写"在线"进行?

三、第二代:触发器与日志表——pg_repack 的巧劲

既然重写期间业务不可能停下来,那就让变更"记下来,事后补"。这是 pg_repack 的核心思路,也是它作为第二代方案的灵魂。这个工具 2011 年从更早的 pg_reorg 项目更名而来,至今迭代到 1.5.3,支持 PostgreSQL 9.5 到 18。

它的执行过程可以讲成一个七步故事:

  1. 先建一张"日志表";
  2. 给原表挂上一个 AFTER 触发器,此后这张表上发生的每一次插入、更新、删除,都会被同步记进日志表——这就是它的"增量捕获"机制;
  3. 与此同时,pg_repack 把原表里的全部存活行拷贝到一张新表里(可选按索引或指定列排序);
  4. 在新表上重建所有索引;
  5. 日志表里已经累积的变更,被一批批"回放"到新表上;
  6. 新表追上进度后,短暂加锁,通过系统目录把新旧表(连同索引和 TOAST 表)瞬间交换;
  7. 删掉旧表,收工。

聪明的读者已经看到了关键:ACCESS EXCLUSIVE 锁只在开头建日志表、结尾换文件这两个瞬间出现,中间的漫长拷贝阶段只持 SHARE UPDATE EXCLUSIVE 锁——普通 DML 照常执行,只有 DDL 会被挡住。锁窗口从"全程"压缩到了"毫秒级的两头",这就是在线重写的全部秘密。看一组实际用法:

# 安装与启用:二进制编译安装 + 库内扩展(两者版本必须匹配)
make && sudo make install
psql -d your_db -c "CREATE EXTENSION pg_repack"

# 在线回收空间(--no-order 即等价 VACUUM FULL)
pg_repack --no-order -t orders your_db

# 在线聚簇(默认按表已配置的聚簇索引排序)
pg_repack -t orders your_db

# 按任意列排序重写
pg_repack --order-by "created_at" -t orders your_db

# 在线搬迁表空间(--moveidx 连索引一起搬)
pg_repack -d your_db -t orders -s fast_tbs --moveidx

# 先试跑预览再正式执行
pg_repack -d your_db -N

代价当然也有,而且不小:

  • 必须有主键(或 NOT NULL 唯一索引)。触发器回放变更时需要靠主键定位行,没有主键的表直接被拒之门外;
  • 触发器带来写放大。重写期间,每一次 DML 都要额外写一条日志表记录,对写入密集的表是不小的负担,极端情况下日志积压甚至会"追不上"(1.5.x 提供了 --switch-threshold、--apply-count 等参数缓解);
  • UNLOGGED 表、临时表不支持,也不能按 GiST 索引聚簇,声明式分区父表无法整体处理;
  • 作为第三方扩展,它要求客户端二进制与库内扩展版本严格匹配,故障后还可能残留临时对象需要手工清理。

即便如此,pg_repack 依然是 PG18 及以下版本做在线重写的事实标准。它的局限也很清晰:所有能力都建立在"触发器 + 日志表"这个外部机制上——这像是一套加装的脚手架,能用,但始终不是数据库自己的一部分。

四、第三代:内核的逻辑解码方案——REPACK

PG19 带来的 REPACK,把这件事从"加装脚手架"变成了"原生能力"。

先说命名。VACUUM FULL 和 CLUSTER 这两个命令其实做的是同一件事——重写表,但一个名字让人误以为是 VACUUM 的加强版,另一个借用了商业数据库"聚簇"的模糊概念,功能重叠又互相混淆。PG19 干脆把它们统一成一个语义清晰的命令:REPACK——重写表以回收磁盘空间。旧命令保留兼容,但新名字承载了新机制。

REPACK 的普通模式与第一代无异(全程独占锁),真正亮眼的是 CONCURRENTLY 选项——它把 pg_repack 的"增量捕获"机制换成了内核自带的逻辑解码:

  1. 创建临时逻辑复制槽,开始把表数据(忽略死元组)拷贝到新文件;
  2. 拷贝期间,业务 DML 完全不阻塞,发生的变更通过逻辑解码被暂存到临时文件;
  3. 数据拷贝完成后,把暂存的增量变更回放到新文件上;
  4. 此时才请求 ACCESS EXCLUSIVE 锁——只用于交换新旧表与索引文件;
  5. 交换完成,锁释放。

和 pg_repack 相比,最大的变化在于:不再需要触发器,也就没有 DML 写放大;锁窗口理论上被压缩到"交换文件"的毫秒级。如果重写期间表的变更量巨大,回放阶段仍会持锁,锁时间可能从毫秒级拉长到分钟级——这是它唯一的"软肋",但可以通过错峰执行规避。实际用法如下:

-- 普通模式(等价 VACUUM FULL)
REPACK orders;

-- 聚簇(等价 CLUSTER)
REPACK orders USING INDEX orders_pkey;

-- 无锁重写(生产首选),完成后自动 ANALYZE
REPACK (CONCURRENTLY, ANALYZE, VERBOSE) orders;

-- 全库:当前用户有 MAINTAIN 权限的所有表(不能在事务块内执行)
REPACK;

-- 实时查看重写进度与阶段(catch-up = 回放变更,swapping relation files = 锁窗口)
SELECT pid, relid::regclass AS tbl, command, phase,
       heap_blks_scanned, heap_blks_total
FROM pg_stat_progress_repack;

作为内核公民,它带来了一整套配套能力:

  • 权限模型:不再是 superuser/owner 特判,而是标准的 MAINTAIN 权限,符合 PG15 以来的细粒度权限体系;
  • 监控内建:pg_stat_progress_repack 视图实时报告进度,且能看到阶段流转——catch-up(回放并发变更)、swapping relation files(锁窗口)等,锁是否异常一目了然;
  • 参数配套:max_repack_replication_slots(默认 5,仅启动时可调)限定并发 CONCURRENTLY 的数量,每个并发重写占一个逻辑复制槽。

当然,新机制也带来新的边界:CONCURRENTLY 不支持 UNLOGGED 表、分区表、没有主键或索引型 replica identity 的表,也不能在事务块内执行——逻辑解码需要行标识,也需要复制槽,这是机制本身的物理约束。

五、给 DBA 的迁移建议

  • PG18 及以下,没有 REPACK 可选:能停窗就用 VACUUM FULL/CLUSTER;需要在线就用 pg_repack,前提是表有主键、非 UNLOGGED、重写期间无 DDL。
  • PG19,默认切换到 REPACK (CONCURRENTLY):内核原生、无扩展依赖、无触发器开销、监控内建,锁窗口最小。升级后建议先拿一张大表冒烟,对比它和 pg_repack 的耗时与锁时间。
  • 仍要保留 pg_repack 的场景:表空间在线迁移(--tablespace)、按任意列排序(--order-by),这两项 REPACK 目前没有等价选项;另外 CONCURRENTLY 要求表已配置 replica identity,未配置的表只能用普通模式。
  • 两边都不支持的:UNLOGGED 表的在线重写、声明式分区父表的整体在线重写——这类场景只能接受停窗,或对每个叶子分区逐个处理。
  • 别忘了 max_repack_replication_slots 只在启动时生效,升级前就要在配置里定好并发预算。

结语

回看这条演进线,其实是一个"把重写变成一等公民"的过程:第一代用锁解决并发,简单但粗暴;第二代用触发器把锁窗口挤到两端,却背上了主键约束和写放大;第三代用内核逻辑解码,把在线重写做成了数据库的原生能力。锁粒度越来越小、能力越来越内建——这是 PostgreSQL 表维护工具过去十几年的演化主线,也是 REPACK 值得每一个 DBA 在 PG19 升级计划里留一个位置的原因。

Robust MPC 4 (鲁棒&随机 MPC) Robust MPC 阅读详情

相关推荐

STM32F407 BootLoader实战:从零搭建到OTA升级(附完整源码)

本文详细介绍了为STM32F407微控制器从零搭建BootLoader并实现稳定OTA升级的完整实战指南。内容涵盖Flash分区规划、链接脚本配置、核心跳转逻辑、中断向量表重定向、可靠的升级流程设计,并深入探讨了双备份与回滚机制以提升工业级可靠性。文章提供了可直接使用的代码框架,并分享了关键调试技巧,帮助开发者快速掌握STM32远程固件更新技术。

weixin_29192211的博客 445

两个月了,第一批IvorySQL布道者已出炉!

6 月末,IvorySQL 社区正式发起了。我们初衷很简单:不搞硬核门槛、不用全职投入,给所有热爱开源、喜欢折腾技术的小伙伴,一个业余参与社区、展示自己、收获专属福利的平台。该计划发布后,立刻吸引了三十多位小伙伴参与。转眼两个月过去了,成果如何呢?先公布结果:已经有 12 位小伙伴达到了 100 积分,迈过了布道者门槛,成为 IvorySQL 第一批官方认证布道者!他们分别是:尚雷,尹海文,杨向博,德哥,杨宇,付超,张晨,葛云杰,魏波,严少安,崔鹏,马顺华。

IvorySQL的博客 172

终极版:完美实现在PyQT5或Pyside2中构建无边框圆角阴影窗口,可拖动,阴影随窗口活动状态变化,窗口鼠标拖动改变大小(完美复用)

终极版(完美复用):完美实现在PyQT5或Pyside2中构建无边框圆角阴影窗口,可拖动,阴影随窗口活动状态变化,窗口鼠标拖动改变大小

文野海阳的博客 1668

fast-lio2复现跑avia数据集

本文详细介绍了在Windows WSL2中安装Ubuntu 22.04并配置ROS2 Humble环境的全过程,涵盖系统初始化、ROS2安装、Livox雷达驱动编译及FAST-LIO2 ROS2版本的构建与验证。通过完整数据集回放与建图测试,成功实现点云地图生成与轨迹记录,并利用evo工具完成轨迹分析与可视化。最终输出包括累积点云地图、位姿轨迹及实验配置文件,为后续精度评估与复现提供基准。

chase_my_dream的博客 369

拉普拉斯矩阵

1、预备知识 拉普拉斯矩阵定义为:L=D-W。其中,D为度矩阵,W为权重矩阵。 (1)D是对角矩阵,对角线上的元素时节点的度,即所有与该节点相关联的边的权重之和;对角线以外的元素为0。有向图的对角矩阵分为 出度矩阵 和 入度矩阵。 图1.1 无向图中的度矩阵 (2)W在无向图中是对称矩阵,在有向图中是非对称矩阵。 ...

zzhe 1万+

解决linux 2.5G网卡驱动问题

解决linux 2.5G网卡驱动问题 关键词:2.5G网卡驱动,ubuntu,安装,2.5G Ethernet LINUX driver,Realtek网卡驱动 由于2.5G网卡较新,各linux发行版的通用驱动已不适用,新装的系统会出现无法上网问题,由于国内网络环境去Realtek官网很难下载到驱动文件,故本文提供github上的2.5G Ethernet LINUX driver r8125 for kernel up to 5.6的版本驱动文件r8125-9.003.05.tar.bz2各位可以自

poorguy_aos的博客 1万+

linux学习

sudo make install:组装好的家具(编译好的库文件),原本放在你的临时工作区。坏处是,你的程序体积会变大。于是,他会根据你的计划书,结合你电脑的当前情况(比如装了什么编译器,库在哪),生成一本极度详细的《施工进度表》。很多大牌的第三方库(比如你刚装的 libevent)为了照顾不同的需求,会同时提供动态库(.so)和静态库(.a),但并不是所有库都这样。只有动态库:常见于系统的核心库(比如你系统的底层 C 库),或者一些商业闭源软件(厂商只给你一个黑盒子,不让你直接把代码打包进你的程序)。

d_mail的博客 194

GBT 27930-2015/2023国标充电协议A类CAN报文ID说明

国标充电CAN的ID,仅供学习参考。

qqq080的博客 7820

ubuntu 偶发启动失败问题分析

ubuntu 系统启动偶发异常问题分析

平面到立体 193

【HarmonyOS】ArkUI的组件扩展

ArkuI的组件扩展主要是通过等装饰器来对于一些常用的自定义组件进行封装, 来简化UI开发流程, 提高自定义组件的可复用性。

2302_79763237的博客 1170

在ZYNQMP上点亮800x480 LCD屏:从framebuffer到DRM框架的完整驱动移植实战

本文详细介绍了在ZYNQMP平台上点亮800x480 LCD屏的完整驱动移植实战,从framebuffer到DRM框架的技术实现。内容涵盖硬件配置、设备树设置、显示时序参数调试以及DRM驱动核心实现,帮助开发者快速掌握PetaLinux系统中的LCD驱动开发技巧。

weixin_33701294的博客 427

Docker Compose:搭建本地多服务开发环境

从一个 Java API、PostgreSQL 和 Redis 开始,配置服务名、端口、环境变量、健康检查、数据卷和依赖关系。理解 Compose 文件如何被解析、项目如何隔离,以及哪些数据不能依赖“重建容器”保留,能够把个人开发环境整理成团队可复用的启动入口。

m0_62221377的博客 477

Allegro172版本如何在联机模式下使用skill文件操作指导

注意:如果下次重新开启Symphony模式的时候可能导致symphony_skill中的文件内容被清空,所以最好将自己常用的skill文件都写到symphony_skill.txt中去,然后做好备份,需要的时候,在开启软件后把文件放到env目录中去。在用Allegeo172版本进行PCB设计的时候,有时会用到Symphony模式进行多人实时在线协同设计,如下图。这个模式设计起来可以提升设计速度,但是也有一个缺陷,就是不能使用skill文件。如下图,当调用skill的时候就会出现如下报错。

weixin_54787054的博客 1589

AI全栈实战 | 3.9-01 爬虫入门:requests 加 BeautifulSoup 三分钟跑通,但 90% 的人栽在这 4 个“礼貌“问题上

⚠️:本文内容仅用于。请遵守目标站点的服务条款与相关法律法规,不得对目标服务造成负担,不得采集、传播个人隐私数据。技术是中性的,边界在你手里。爬虫的入门技术(requests 发请求 + BeautifulSoup 解析)三分钟就能学会。真正区分"会用爬虫"和"会做爬虫的人"的,是这篇我们跑通"请求→解析→存储"的最小闭环,然后重点把"合规与礼貌"这根弦给你绷上。

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 432

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【基于 WebSocket 的多用户网页五子棋】—— 数据管理模块

但行好事,莫问前程

qq_42996461的博客 250

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C++算法(七)

C++ stack算法题和一些客观题

2503_94479566的博客 378

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

OpenClaw 深度调查:把 AI 助手做成你自己的控制平面

对 openclaw/openclaw 的完整技术尽调:Gateway 架构、Agent 运行时、工具与沙箱、30 个消息通道、181 个插件、安全信任边界、工程代价与采用建议。17 章 / 10 张图,全部数字标注口径,数据截至 2026-10-31。

厚积薄发 294

IDEA 简介:从零认识 IntelliJ IDEA

IDEA 全称,是由 JetBrains 公司开发的一款主流 Java 集成开发环境(IDE)。它集代码编辑、编译、调试、测试、版本控制、构建管理等功能于一体,被大量 Java 开发者视为日常开发的首选工具。所谓 IDE(Integrated Development Environment,集成开发环境),就是把写代码所需的各类工具整合到同一个界面中。相比只用文本编辑器加命令行编译,IDE 能显著减少重复操作,让开发者把精力集中在业务逻辑上。

ztI10的博客 177

TransactionInactiveError:await 之后事务已经不活跃了

TransactionInactiveError 出现于 IndexedDB 事务已结束(非 active 状态)时调用 put、get 等操作。常见于 await 后或 setTimeout 中执行请求,因任务已完成,事务已提交或进入 finished 状态。请求必须在创建事务的同一任务中挂起,否则无效。解决方法:在 onsuccess 回调内同步发起后续操作,或提前将所有请求挂入事务。避免在异步上下文中使用事务操作,确保请求在事务活跃期内完成。

qq_40822000的博客 159

云计算课程小组PPT汇报任务书

36人分为6组,每组6人;形式:生活案例调研 + 理论解读;汇报时长7‑9min;PPT建议10‑14页。

Michelle8023的博客 208

大表 ALTER TABLE 怎么做?NineData OnlineDDL 配置与执行实践

介绍NineData OnlineDDL大表变更实践,涵盖执行策略、切表控制、拷贝批次与可视化进度追踪。

dbasenewbie的博客 295

软件项目交付时,源代码之外还应该交付哪些技术资料?

软件项目交付时,除可运行程序外,还应核对源代码、数据库结构、接口文档、部署运维、测试验收和账号资产。本文从实际交付环节拆解完整资料清单,并说明合同如何约定交付范围、知识产权与维护边界。

chengduyoushu的博客 306

Axum 初步入门学习笔记

)自定义错误实现后,可以直接作为Result的ErrAxum 是一个构建在 Tokio、Tower、Hyper 之上的 Rust Web 框架,核心能力是:Router 路由、Extractor 提取器、Tower 中间件、IntoResponse 响应机制、State 共享状态。那张分层架构图是“地图”不是“说明书”:它帮你建立全局观,但 Service、Repository、数据库、Redis、MQ 都是你自己在 Handler 内部调用的业务/基础设施层,不是 Axum 自带的。

weixin_43219667的博客 187

MySQL报Too many open files?句柄耗尽的排查与参数配置

文章讲清 max_connections 与 open_files_limit 两个额度、句柄被谁占走、分区表为什么最容易踩,给出从 error log 到 /proc/pid/limits 的定位顺序与句柄预算算法。

「一个在数据库赛道上边学边分享的学妹」——聚焦国产数据库/信创方向、分享学习笔记、踩坑经验和行业观察。 336

SQL Server 数据库双备份:简化配置,兼顾本地留存与异机归档

本文针对中小企业SQLServer数据库备份难题,提出简化配置与本地+异机双副本方案。传统原生备份依赖手动脚本,存在配置复杂、传输易中断、日志分散、静默失败等问题。推荐使用图形化工具实现备份、本地留存与异机同步一体化管理,支持断点续传、加密传输、自动清理与版本控制,降低运维负担。双副本策略提升数据安全性,本地快速恢复,异地防硬件故障。建议定期验证恢复流程,确保备份可用性。

2501_93776716的博客 291

Redis 入门详解:核心特性、重要文件与全局命令实战

本文系统介绍了Redis的核心特性、重要文件及全局命令。Redis凭借内存存储、C语言实现和单线程设计,具备极高的读写性能;支持持久化(RDB/AOF)、主从复制与集群架构,适用于缓存、排行榜、计数器等场景,但不适合存储海量冷数据。文中还解析了常见面试题,如为何快速、单线程原理、持久化机制、KEYS *禁用原因及过期键处理策略,强调其在高并发场景下的优势与局限。

2303_77004089的博客 206
上一篇: PostgreSQL 日报|GiST 重扫误标死元组(8 月 17 日)
下一篇: PostgreSQL 日报|备份单页损坏被静默忽略(8 月 18 日)
IvorySQL
博客等级 码龄5年 745粉丝 565原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值