PostgreSQL 又砍掉一笔“隐形税”:这个 VACUUM 小改动,为什么值得所有 DBA 和开发者重视?
很多系统,不是慢在 SQL,不是慢在磁盘,而是慢在一堆“本来就不用做”的后台动作上。 PostgreSQL 最新这个 commit,砍的就是这种最容易被忽视、却会长期吞噬维护成本的“隐形税”。
先说结论:这不是一个“VACUUM 大提速”的营销型优化,而是一个非常典型的 PostgreSQL 式内核优化:让明知没有收益的工作,直接别发生。
这次提交 01b7e4a46d0fa8478c1142f65ee441fb0c32cca1 做的事情很克制,但很值钱。
提交信息已经把问题说透了:
SKIP_PAGES_THRESHOLD 这类扫描策略优化stale prune XIDheap_page_prune_and_freeze() 可能会被调用到一些实际上根本没有 pruning/freezing 工作可做的页面上于是新逻辑加了一个 fast path:
all-frozen,直接早退all-visible,并且这轮不会尝试 freezing,也直接早退DISABLE_PAGE_SKIPPING,那就不能这么干这就是典型的 PostgreSQL 优化哲学: 不是拼命把“必要工作”做快,而是优先识别“不必要工作”,然后一刀砍掉。
因为数据库系统真正昂贵的,不只是执行器里的前台 SQL。 后台维护同样在抢资源,而且是持续抢:
从第一性原理看,只要一个动作不产生收益,它就应该被消灭。 哪怕它单次只浪费一点点判断、检查、函数调用、页级处理路径开销,乘上:
它就会变成一笔稳定复利的系统性成本。
所以这次改动真正优化的,不是“SQL 跑得更快”,而是维护路径更不浪费。
all-visible 不等于 all-frozen很多人嘴上都说知道 visibility map,但真到讨论 VACUUM 行为时,概念经常混。
PostgreSQL 的 visibility map,对每个 heap page 维护 2 个 bit:
all-visibleall-frozen这两个 bit 的意义完全不同。
all-visible 是什么?表示这个页面上的 tuple 对所有事务都可见。 这意味着:
all-frozen 又是什么?这是更强的状态。
表示这个页面上的 tuple 不只是“都可见”,而且都已经冻结。
而且它只能建立在 all-visible 基础上。
这有什么价值? 价值在于:即便是为了防止 XID wraparound 的 aggressive VACUUM,也可以跳过这些页面。
一句话概括:
all-visible 解决的是“要不要看这个页”all-frozen 解决的是“连防腐式的大扫除都可以不看这个页”这正是这次 commit 值得写文章的地方。
很多人以为,只要页面被标成 all-visible 或 all-frozen,VACUUM 就一定不会碰。
错。
PostgreSQL 为了整体扫描效率,并不是机械式“见到可跳过页就跳”。
SKIP_PAGES_THRESHOLD代码里有一个非常关键的阈值:SKIP_PAGES_THRESHOLD = 32。
什么意思? 如果一段可跳过页面的连续区间太短,PostgreSQL 可能宁可继续顺序扫描,也不选择跳页。 原因不是偷懒,而是工程理性:
也就是说, “理论上能跳过”不代表“实现上就一定跳过” 。
即使页面已经 all-visible,如果它还不是 all-frozen,正常 VACUUM 仍可能去扫它。
为什么?
因为 PostgreSQL 会做一件事:提前冻结。 它希望把未来 aggressive VACUUM 必须承担的冻结债务,尽量在平时就消化掉。
所以,正常 VACUUM 访问 all-visible but not all-frozen 页面,并不反常,反而是为了把未来的大成本打散到今天的小成本里。
答案很精准:减少“明知没有 pruning/freezing 收益,却仍然进入 heap_page_prune_and_freeze() 流程”的无效开销。
这不是改 VACUUM 大框架。 不是改 visibility map 语义。 不是改冻结策略。 不是改查询优化器。
它只是识别出一个很典型的问题:
有些页面虽然最终什么都不用做,但流程还是跑进去了。
于是现在直接在入口前加一道判断:
all-frozen 的页,没必要再折腾all-visible 且本轮根本不会 freeze 的页,也没必要再折腾DISABLE_PAGE_SKIPPING这也很好理解。
DISABLE_PAGE_SKIPPING 本来就是在怀疑页面跳过逻辑或可见性信息不该被信任时才用的。
既然你都说“不要跳页了”,那当然也不能再基于这些状态做早退。
这类优化最适合下面这些对象:
all-visible / all-frozen 页的数据集这些表的共同特征是: 页很“干净”,但维护系统仍可能偶尔误入处理路径。
过去的情况是:
现在则是:
如果你的表是下面这种风格,这次优化就不该被吹成“性能革命”:
UPDATE/DELETE在这些场景里,页面确实有 pruning/freezing 工作。 那 fast path 自然不会替你省下太多。
换句话说,这次改动的收益高度依赖前提条件:
前提一变,结论就得变。 这才是严肃讨论数据库优化该有的逻辑。
不要再把 autovacuum 只当成“不得不做的清洁工”。 它本质上是数据库的性能资产管理系统。
visibility map 越健康,all-visible / all-frozen 页越多,后续:
这次 commit 释放的信号很明确: PostgreSQL 越来越在意维护路径上的“无效动作”了。
你写出来的不只是 SQL,也是数据库的维护成本曲线。
如果你的业务模型和访问模式总是在制造:
那你不是只在拖慢查询, 你是在给 VACUUM、冻结、膨胀治理持续加税。
开发者常见的错觉是: “后台维护是 DBA 的事。”
不,后台维护从来都是应用行为的镜子。
因为真正决定数据库能否长期稳定跑下去的,往往不是某个 headline feature, 而是这种看起来不轰动、但持续压缩复杂性成本的工程改进。
PostgreSQL 社区这次的处理非常典型:
这才是成熟数据库内核最值钱的能力。 不是“做更多”,而是知道哪些事根本不该做。
如果把数据库系统比作一家公司,很多系统不是死于一次重大事故, 而是死于每天都在发生、却没人盯上的小浪费。
这个 PostgreSQL commit 的价值,就在于它继续把这样一笔“隐形税”做薄了。 它不会让你一夜之间 benchmark 翻倍, 但它会让数据库在真实生产环境里,少一点无意义消耗,多一点长期稳定性。
你怎么看?
你在线上更担心 PostgreSQL 的前台 SQL 性能,还是后台 VACUUM / 冻结 / 膨胀 这些“慢性成本”?欢迎留言聊聊。