PostgreSQL 19 深度前瞻:原生图查询、零停机 REPACK 与并行 Autovacuum,2026 年最值得升级的数据库大版本
2026 年 4 月 8 日,PostgreSQL 19 正式进入特性冻结(Feature Freeze),Beta 已经放出,正式版锁定 2026 年 9 月。这一版的分量,可能是最近五年里最重的一次:SQL/PGQ 原生图查询进内核、
ON CONFLICT DO SELECT补齐 Upsert 最后一块拼图、REPACK CONCURRENTLY让 DBA 告别凌晨三点锁表、autovacuum 终于学会并行干活、64 位 MultiXact 干掉一个折磨了社区十几年的"要么 vacuum 要么死"的故障模式。这篇文章不做新闻搬运。我会按"这个特性解决什么真实痛点 → 底层怎么实现 → 生产上怎么用 → 有什么坑"的思路,把 PG 19 值得你关注的东西一个一个拆开讲透。
一、先说结论:谁应该关注 PG 19
如果你符合以下任意一条,PG 19 对你来说不是"可升可不升",而是"值得排进 2026 Q4 的规划":
- 业务里有图状关系查询(社交关系、风控图谱、推荐链路、权限继承树),目前靠递归 CTE 硬写,或者额外维护了一套 Neo4j —— SQL/PGQ 可能让你把图数据库直接下线。
- 有高并发 Upsert 场景(去重表、幂等写入、"获取或创建"模式)——
ON CONFLICT DO SELECT把原来要两条 SQL + 重试循环的逻辑压缩成一条原子语句。 - 被表膨胀折磨过,做过
pg_repack,或者半夜执行过VACUUM FULL——REPACK CONCURRENTLY是内核原生的在线表重组,不再需要外部扩展。 - 大表 autovacuum 追不上写入速度 —— 并行 autovacuum + 表优先级机制直接命中这个痛点。
- 凌晨被 MultiXact 成员耗尽的告警叫醒过 —— 64 位 MultiXact 让这个故障模式成为历史。
下面逐个拆。
二、SQL/PGQ:关系型数据库的"图思维"革命
2.1 这是什么,为什么重要
PG 19 最受瞩目的特性,是对 SQL/PGQ(Property Graph Queries,ISO SQL:2023 第 16 部分) 的原生支持。一句话概括:你可以把现有的关系表声明为一张属性图,然后用图模式匹配语法直接遍历它,不需要迁移数据,不需要额外部署图数据库。
在此之前,PostgreSQL 里做图遍历只有两条路:
- 递归 CTE(
WITH RECURSIVE):能用,但表达"变长路径 + 多种边类型 + 环检测"时,SQL 会膨胀成一堵没人敢碰的墙。 - 外挂图数据库(Neo4j、NebulaGraph 等):引入了双写一致性、数据同步延迟、多一套运维体系三座大山。
SQL/PGQ 的思路是"图是关系数据的一种视图"。你的点(vertex)和边(edge)本来就存在普通表里,图定义只是给它们贴上语义标签。
2.2 上手:从建表到图查询
先准备一个典型的社交场景——用户和关注关系:
CREATE TABLE users (
id bigint PRIMARY KEY,
name text NOT NULL,
city text
);
CREATE TABLE follows (
follower_id bigint REFERENCES users(id),
followee_id bigint REFERENCES users(id),
since date,
PRIMARY KEY (follower_id, followee_id)
);
在 PG 19 里,把这两张表声明为一张属性图:
CREATE PROPERTY GRAPH social_graph
VERTEX TABLES (
users KEY (id)
LABEL person
PROPERTIES (id, name, city)
)
EDGE TABLES (
follows KEY (follower_id, followee_id)
SOURCE KEY (follower_id) REFERENCES users (id)
DESTINATION KEY (followee_id) REFERENCES users (id)
LABEL follows
PROPERTIES (since)
);
注意几个关键点:
VERTEX TABLES/EDGE TABLES只是元数据声明,不复制数据,不建新存储。底层还是那两张堆表。- 边表必须声明
SOURCE和DESTINATION,本质上就是把外键关系升格为图语义。 - 一张表可以挂多个
LABEL,同一个图里可以有多种点和边。
然后就是重头戏,GRAPH_TABLE 查询——找出"张三关注的人所关注的人"(二度人脉):
SELECT gt.*
FROM GRAPH_TABLE (
social_graph
MATCH (a IS person WHERE a.name = '张三')
-[IS follows]-> (b IS person)
-[IS follows]-> (c IS person)
WHERE c.id <> a.id
COLUMNS (c.name AS fof_name, c.city AS fof_city)
) gt;
对比一下等价的递归 CTE 写法(这还只是二度,固定深度都这么啰嗦):
SELECT DISTINCT u3.name, u3.city
FROM users u1
JOIN follows f1 ON f1.follower_id = u1.id
JOIN follows f2 ON f2.follower_id = f1.followee_id
JOIN users u3 ON u3.id = f2.followee_id
WHERE u1.name = '张三' AND u3.id <> u1.id;
二度还能忍。但当需求变成"3 到 5 度以内、途经至少一个'认证用户'、路径上不许出现被拉黑的边",JOIN 写法直接爆炸,而图模式语法只是在 MATCH 里多写几个量词和条件。
2.3 实现原理与性能预期
理解 SQL/PGQ 在 PG 19 里的执行方式非常重要,因为它决定了你的性能预期:
GRAPH_TABLE 在解析阶段被重写为普通的 JOIN 树。 也就是说,图查询最终走的还是 PostgreSQL 那套成熟的优化器和执行器——该用 Hash Join 用 Hash Join,该走索引走索引。
这带来两个直接推论:
- 好消息:你之前所有的调优经验都有效。边表上
(follower_id)和(followee_id)两个方向的索引照建,work_mem、并行查询照调,EXPLAIN照看。 - 要清醒的地方:它不是 Neo4j 那种以邻接遍历为原生存储模型的引擎。超深路径(十几跳)、超大规模全图算法(PageRank、社区发现)依然不是它的主场。PG 19 的 SQL/PGQ 首发版本主要覆盖固定/有限深度的模式匹配,定位是干掉"80% 的轻中度图需求"——恰恰是大多数业务实际需要的那部分。
我的判断:对于"主数据在 PG、图需求是查询而非全图计算"的团队,SQL/PGQ 落地后,单独的图数据库会越来越难以证明自己的存在必要。少一套存储、少一条同步链路、事务原生一致,这三条在架构评审上几乎是降维打击。
三、ON CONFLICT DO SELECT:Upsert 拼图的最后一块
3.1 "获取或创建"的老大难
这个需求每个后端工程师都写过一百遍:插入一行,如果已经存在,就把已存在的那行拿回来。典型场景:根据 email 拿用户 ID、标签去重入库、幂等事件登记。
PG 19 之前的主流写法是这样:
INSERT INTO tags (name) VALUES ('postgres')
ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.name
RETURNING id;
看出别扭了吗?为了拿回已存在行的 id,你被迫做了一次假更新——SET name = EXCLUDED.name 把值更新成它自己。这个 hack 的代价是真实的:
- 产生一个新的行版本(dead tuple),白白制造表膨胀;
- 触发行级锁和 WAL 写入,高并发下是实打实的开销;
- 会触发
ON UPDATE触发器,可能引爆意料之外的业务逻辑。
另一派写法是先 SELECT 再 INSERT ... ON CONFLICT DO NOTHING 再 SELECT,三条语句加重试循环,代码丑且仍有竞态窗口要小心处理。
3.2 PG 19 的答案
INSERT INTO tags (name) VALUES ('postgres')
ON CONFLICT (name) DO SELECT
RETURNING id, name;
语义非常干净:插入成功返回新行;冲突则直接返回已存在的行,不产生任何写放大。没有假更新、没有 dead tuple、没有触发器副作用。
更关键的是它继承了 ON CONFLICT 家族的核心承诺:无竞态。与 SQL 标准的 MERGE 不同,ON CONFLICT 在并发场景下保证操作结果必然是"插入"或"取回"之一,不会因为并发事务的插入/删除时序问题而报错或丢失。这就是为什么 PostgreSQL 社区在已有 MERGE 的情况下仍然坚持扩展 ON CONFLICT——两者的并发语义根本不同。
还可以带锁语义,配合后续更新:
-- 拿回已存在行并锁住它,防止后续事务修改
INSERT INTO accounts (email) VALUES ('dev@example.com')
ON CONFLICT (email) DO SELECT FOR UPDATE
RETURNING id;
3.3 生产建议
- 存量代码里所有
DO UPDATE SET x = EXCLUDED.x的假更新写法,升级后应该系统性地替换为DO SELECT。在写入热点表上,这能直接降低膨胀速率和 WAL 量。 - ORM 层面(SQLAlchemy、Prisma、GORM 等)预计会在 PG 19 GA 后跟进适配,但在那之前用原生 SQL 完全值得。
四、REPACK CONCURRENTLY:告别凌晨三点的锁表窗口
4.1 表膨胀,DBA 的慢性病
PostgreSQL 的 MVCC 决定了 UPDATE/DELETE 不会立即释放空间,死元组靠 vacuum 回收。但 vacuum 只能把空间标记为可复用,无法把已经膨胀的物理文件缩回去。一张频繁更新的 500GB 表,实际有效数据可能只有 150GB,剩下 350GB 是碎片——白吃磁盘、白吃 buffer cache、拖慢顺序扫描。
治疗手段一直很尴尬:
VACUUM FULL:重写全表,但持有ACCESS EXCLUSIVE锁,锁表期间业务完全中断,大表意味着几十分钟到几小时的停机;pg_repack扩展:能在线做,但是外部工具,需要额外安装、有版本兼容矩阵、出问题时的排障资料远不如内核特性丰富。
4.2 内核原生方案
PG 19 引入 REPACK 命令,并且支持 CONCURRENTLY:
-- 在线重组表,业务读写照常进行
REPACK TABLE CONCURRENTLY my_big_table;
-- 按某个索引的顺序重排物理存储(类似 CLUSTER,但在线)
REPACK TABLE CONCURRENTLY my_big_table USING INDEX idx_created_at;
实现思路可以概括为"新文件重建 + 逻辑解码追增量":
- 给表打快照,把有效数据顺序写入一个全新的物理文件(这一步顺带完成排序和碎片消除);
- 重建期间业务产生的增量变更,通过逻辑解码机制持续回放到新文件;
- 增量追平后,在一个极短的锁窗口内完成新旧文件切换。
整个过程中只在最后切换那一瞬间需要短暂加锁,对业务的感知接近于零。这套思路和 pg_repack 的触发器方案、以及 MySQL 的 gh-ost 是同一个流派,但由内核原生实现,意味着与 WAL、复制、备份工具链的兼容性从根上就是对齐的。
4.3 注意事项
- REPACK 期间磁盘上会同时存在新旧两份文件,峰值需要约 2 倍表大小的空闲空间,容量规划要提前做;
- 逻辑解码追增量的机制决定了:写入极端热的表,追平窗口会拉长,建议避开写高峰执行;
- 从
pg_repack迁移过来的团队,可以逐步把 cron 里的外部工具调用替换为内核命令,少维护一个扩展的兼容矩阵。
五、Autovacuum 的两次进化:并行 + 优先级
5.1 并行 autovacuum:解除了十几年的封印
手动 VACUUM (PARALLEL 4) 早在 PG 13 就支持并行清理索引,但 autovacuum 一直被禁止使用并行——这是个历史遗留的保守设定。结果就是:一张有十几个大索引的表,autovacuum 串行逐个清理索引,慢到追不上死元组的产生速度,膨胀风险持续累积。
PG 19 新增:
-- 全局:允许 autovacuum 最多使用 4 个并行 worker 清理索引
SET autovacuum_max_parallel_workers = 4;
-- 表级:针对索引多的大表单独放开
ALTER TABLE orders SET (parallel_workers = 4);
收益最大的场景就是"宽表 + 多索引":索引清理是 vacuum 全程最慢的阶段,并行化之后整轮 vacuum 的墙钟时间可以数倍缩短。
一个必须注意的坑:每个并行 worker 会占用自己的 maintenance_work_mem 份额。如果你把 maintenance_work_mem 调得很大又开了多个并行 worker,内存峰值是乘法关系,容易把机器打爆。升级后要把这两个参数放在一起重新核算。
5.2 表优先级:谁急谁先来
老版本 autovacuum 挑表的顺序基本是"按发现顺序",没有轻重缓急的概念。经典惨案:一张不太要紧的日志表占着 worker 慢慢清,核心订单表眼睁睁膨胀到触发事务 ID 告警。
PG 19 引入了 autovacuum 优先级机制,配套 pg_stat_autovacuum_scores 视图,让你能直接看到每张表在调度器眼里的"焦虑分数":
SELECT relname, score, last_autovacuum
FROM pg_stat_autovacuum_scores
ORDER BY score DESC
LIMIT 10;
膨胀风险第一次变成了可观测、可排序、可干预的指标,而不是靠 DBA 的经验和几段第三方监控 SQL 拼凑。
5.3 64 位 MultiXact:干掉一个凌晨三点级故障
这个改进不显眼,但老运维看到会直接鼓掌。
背景:当多个事务同时持有同一行的共享锁(SELECT ... FOR SHARE、外键约束检查都会触发),PostgreSQL 用 MultiXact 结构记录"哪些事务锁了这行"。而 MultiXact 的成员计数器是 32 位的——高并发外键写入场景下,40 亿的成员空间是真的能耗尽的。耗尽之后系统拒绝新事务,唯一恢复路径是停应用、做紧急 VACUUM。这就是社区流传的"要么 vacuum,要么死"(vacuum-or-die)故障模式。
PG 19 把成员计数器扩展到 64 位。理论上回卷仍然存在,但 2^64 的空间在实践中意味着这个故障模式彻底消失。如果你没被它坑过,恭喜;如果被坑过,你知道这一条就够你升级了。
六、查询规划提示(Plan Hints):官方终于松口了
PostgreSQL 社区拒绝 hint 拒绝了二十年,官方立场一直是"优化器应该被修好,而不是被绕过"。第三方的 pg_hint_plan 因此成了很多生产环境的必装扩展。
PG 19 的变化:官方 contrib 模块提供计划提示能力。注意两个措辞——"contrib"和"提示":
- 放在 contrib 而不是内核默认行为,表明社区的态度依然克制:这是应急工具,不是常规武器;
- 典型使用场景是"优化器在某个统计信息失真的边角案例里选了灾难性计划,业务等不起统计修复周期"——这时候一个 hint 能救命。
我的建议和社区一致:hint 是止血带,不是日常穿搭。每一个上了 hint 的查询都应该带着注释和工单号,定期回顾能不能摘掉。但"官方提供止血带"本身,是对生产现实的重要让步,值得肯定。
七、其他值得记一笔的改进
时态数据操作(Temporal DML):跟进 SQL:2011 时态标准,对带有效期的数据(价格历史、政策版本)提供原生的时段更新/删除语义,不用再手写"截断旧区间 + 插入新区间"的三段式 SQL。
逻辑复制持续补强:无需重启即可启用 WAL 逻辑解码(wal_level 调整不再需要重启窗口,这对 7×24 业务是重大利好)、复制槽同步延迟监控。逻辑复制正在从"能用"走向"运维友好"。
原生 JSON 导出:pg_dump 家族和 COPY 链路对 JSON 导出的原生支持,数据交换管道少写一层转换脚本。
监控颗粒度:vacuum/analyze 进度视图带上内存使用信息、pg_get_multixact_stats() 等新观测函数——配合前面的优先级分数视图,PG 19 的可观测性是全方位加密的。
jsonb_agg 优化、LISTEN/NOTIFY 性能增强、file_fdw 跳过起始行:都是小刀,但都砍在日常使用的高频路径上。
八、升级实操清单
结合 Beta 阶段已知信息,给一份务实的升级准备清单:
兼容性排查(现在就可以做):
standard_conforming_strings在 PG 19 被永久固定为 on——还依赖老式反斜杠转义行为的祖传代码(十几年前的 PHP 项目重灾区)需要排查;- 检查所有假更新式 Upsert(
DO UPDATE SET x = EXCLUDED.x),列入DO SELECT改造清单; - 用了
pg_repack的,评估切换到原生REPACK CONCURRENTLY的窗口; - 用了
pg_hint_plan的,关注官方 contrib hint 模块的语法差异。
参数重新核算:
-- 升级后建议的初始核算项
autovacuum_max_parallel_workers -- 从 0 开始逐步放开,观察 IO
maintenance_work_mem -- 与并行 worker 数相乘核算峰值内存
灰度节奏建议:9 月 GA 后,先上报表库/只读副本 → 观察一个完整业务周期 → 再动核心 OLTP。PG 的 .0 版本质量一贯很高,但大版本升级的谨慎永远不多余。
九、总结:PG 19 在下一盘什么棋
把 PG 19 的特性列表放远一点看,能看出 PostgreSQL 社区清晰的战略:
- 吞并邻近领域:pgvector 吃向量数据库的份额,SQL/PGQ 吃图数据库的份额。"一个 PostgreSQL 顶过去四五个专用存储"的叙事正在一个版本一个版本地兑现。
- 消灭停机窗口:在线 REPACK、免重启逻辑解码——所有需要"挑时间做"的运维动作都在被改造成"随时能做"。这是云时代数据库的生存底线。
- 把 DBA 的经验固化成机制:autovacuum 优先级分数、并行清理、64 位 MultiXact,全是把"资深 DBA 半夜救火的经验"变成"内核默认就做对的事"。
对开发者,ON CONFLICT DO SELECT 和图查询会直接改变你写代码的方式;对 DBA,这可能是历年来"减少半夜告警"含金量最高的一个版本。
2026 年 9 月,值得把一次升级排进日程表。
本文基于 PostgreSQL 19 Beta 及社区公开资料撰写,个别特性细节以 GA 版 Release Notes 为准。