PostgreSQL 18:异步 I/O 不光是快 3 倍

异步 I/O、UUIDv7、跳过扫描、虚拟生成列——PG 18 是近年来变化最大的一个版本。值得升级吗?

PG 大版本升级,过去一直有两件烦心事:

一是升完了统计信息丢光,优化器两眼一抹黑,ANALYZE 跑完之前查询崩好几天。二是折腾半天,性能提升不明显,不值得冒这个险。

PG 18 把这两件事都动了。

而且这次不是小步迭代——几个改动直接改了 PG 用了二十年的工作方式。

最大的改法:异步 I/O

PG 以前读数据是这样的:发一个 I/O 请求,进程挂起等数据回来,再发下一个。这在机械硬盘时代是自然的——磁盘自己就是串行的。但在 NVMe SSD 面前,CPU 等磁盘的时间越来越不值。

PG 18 引入了异步 I/O 子系统。一次发一堆请求,谁先回来谁先处理,CPU 不再空等。

异步 I/O 原理对比

官方数据:顺序扫描在某些场景下能到 3 倍。不是 30%,是 3 倍。

怎么开?一行配置:

io_method = 'io_uring'

io_uring 是 Linux 5.1+ 的高性能异步接口。内核版本不够也可以用 worker 模式——走工作线程模拟异步,差一点但比老模式强。

这个改动最值钱的地方: 不是跑测试快 3 倍,而是你那些吃顺序扫描的场景——数仓 ETL、全表报表、大表 vacuum、bitmap 扫描——都能吃上。如果业务 IO 密集,升了立竿见影。

UUIDv7:B-tree 不再怕随机 ID

UUID 做分布式主键的好处不用多说:全局唯一、不依赖自增。但 UUIDv4 是纯随机的,往 B-tree 里写的时候,每个新值可能落在任何位置。结果是什么?索引页频繁分裂,写入性能崩得比不用 UUID 还惨。

好多团队因为这个原因又退回自增了。

PG 18 原生支持 UUIDv7——时间戳在前,保证新生成的 UUID 大致有序。B-tree 写入不再分裂。

1
2
3
4
5
-- PG 18 直接用
SELECT uuidv7();

-- uuidv4() 作为正式函数
SELECT uuidv4();

这事换到几年前要靠插件或者应用层拼时间戳才能实现。 现在 PG 内建了。对分布式系统、多数据中心同步、微服务间数据交换的场景,这是个实在的利好——以后可以放心用 UUID 做主键了。

B-tree 跳过扫描:少建一个索引

建了 (a, b, c) 复合索引,查 WHERE b = 1。因为没用到 a 做前缀,优化器不走索引,扫全表。

以前的做法:再建一个 (b) 索引。但索引多了写入慢、占空间。

PG 18 的跳过扫描能在索引里自动"跳过"不参与筛选的前缀列,直接扫描匹配 b 的部分。效果类似:一个复合索引能覆盖更多查询模式。

B-tree 跳过扫描工作原理

对 OR 条件的查询也有优化——以前 OR 很难用到索引,现在优化器能把它转化过来。

这个改动最实用的地方: 生产系统上复合索引一堆,查询模式又杂,不可能给每种查询都建独立索引。跳过扫描相当于用已有的复合索引兜住了更多查询,减少"要不要加个索引"的纠结。

虚拟生成列:省空间的写法

生成列之前只能存下来(STORED),占空间。PG 18 支持虚拟生成列(VIRTUAL)——读取时实时计算,不占存储。

1
2
3
4
5
CREATE TABLE users (
    first_name text,
    last_name text,
    full_name text GENERATED ALWAYS AS (first_name || ' ' || last_name) VIRTUAL
);

而且现在默认就是虚拟的(不写 STORED 或 VIRTUAL 就是 VIRTUAL),和之前正好反过来,迁移时大概率有兼容性影响需要注意。

pg_upgrade 升级体验:两个痛都解决了

以前大版本升级最劝退的地方:统计信息不保留。

升完级,优化器对数据分布一无所知,得重新跑 ANALYZE。几十 TB 的库上,ANALYZE 跑完之前查询性能崩掉,很多团队因为这个不敢升。

PG 18 改了:统计信息在升级时保留。升完级,查询性能基本无缝过渡。

另外 pg_upgrade 加了 --swap 模式——不再 copy/clone/link 数据文件,直接交换目录位置,升级时间大幅缩短。

pg_upgrade --swap --jobs=4

几个值得注意的小变化

  • EXPLAIN ANALYZE 默认显示 BUFFERS — 少敲几个字,之前总忘加 (BUFFERS)
  • 页面校验和默认开启 — 新 initdb 的集群自动启用,不用再单独配
  • RETURNING 支持 OLD/NEW — INSERT/UPDATE/DELETE/MERGE 都能取到变更前后的值,审计和事件溯源场景很有用
  • OAuth 认证 — 对接 SSO 不需要额外插件,对托管平台是好事
  • Wire Protocol 3.2 — 2003 年以来第一次协议版本升级,为后续功能做准备
  • vacuum 主动冻结 — 减少紧急冻结触发的概率,运维轻松一点

升级决策清单

升不升?我的判断

如果业务 IO 密集——大数据量顺序扫描、频繁 vacuum、大量 bitmap 扫描——AIO 这个理由就够了。升级流程也比以前安全:统计信息保留,--swap 速度快得多。

UUIDv7 和跳过扫描不会让你升完当天就看见效果,但在这个版本上做的表结构设计,两三年后回头看会觉得选对了。

大版本升级不是没有风险的。先跑从库或测试环境。

四个最能打的功能:AIO(性能)、UUIDv7(主键设计)、跳过扫描(索引效率)、升级体验(运维成本)。你痛哪个,升哪个。


欢迎分享。如果你有踩坑经验或正在考虑升级,评论区见。