2025 年 9 月 25 日,PostgreSQL 18 正式发布。这是近年改动面最宽的一个大版本:读路径迎来多年来的第一次架构级重构——异步 I/O;优化器补上 skip scan 和一批规则改写;开发者侧新增虚拟生成列、uuidv7() 与 RETURNING 新旧值双取;pg_upgrade 第一次把优化器统计搬进新集群。本文所有特性描述均以 postgresql.org 官方文档为准,按「解决什么问题、怎么用、对升级有什么影响」逐项展开,最后给一份可执行的升级建议。
总览:18 版改了什么
PostgreSQL 每年一个 major 版本,18 的特别之处在于「底层动刀」与「日常省心」同时发生。先看与生产关系最大的特性全景:
| 特性 | 解决的问题 | 用法要点 |
|---|---|---|
| 异步 I/O | 后端读数据页要逐块同步等待,云盘延迟被放大 | io_method = worker(默认)或 io_uring,io_workers 默认 3 |
| skip scan | 复合索引前导列无条件时整条索引用不上 | 尾列等值、前导列低基数收益最大,由 planner 按代价选择 |
| 虚拟生成列 | STORED 列写入时计算,占存储、拖慢写入 | 新建生成列默认 VIRTUAL,表达式限内置函数 |
uuidv7() |
随机 UUID 主键索引局部性差 | 毫秒时间戳有序,可传 interval 偏移 |
| pg_upgrade 保留统计 | 升级后统计清空,执行计划赌运气 | 默认转移,扩展统计需 vacuumdb 补齐 |
| RETURNING OLD/NEW | 改动前后值要查两次或靠触发器 | RETURNING old.col, new.col,别名可改 |
| 时间约束 | 时段重叠校验散落在触发器与代码 | PRIMARY KEY (room, during WITHOUT OVERLAPS) |
| OAuth 2.0 认证 | 企业身份体系接入要靠外部插件 | pg_hba.conf 新增 oauth 方法 |
还有几件容易忽略的事:initdb 默认开启数据校验和;autovacuum_max_workers 不再需要重启即可调整;订阅端并行流式回放从默认关闭改为默认开启;MD5 口令认证开始给出弃用警告。下面挑对生产影响最大的部分展开。
异步 I/O:读路径的架构级拐点
长期以来,PostgreSQL 的每个后端进程都按「一次一个系统调用」的方式读数据:读完一块、处理、再读下一块。PostgreSQL 17 引入 read stream 统一了预读的发起方式,但本质仍是靠 posix_fadvise 提示内核预热页缓存——后端依旧要为每一块数据同步等一次系统调用。18 把 I/O 层重写为异步模型:后端把一批读请求排队提交,I/O 在后台完成,后端继续扫描与计算。官方公告称该子系统在读场景最高带来 3 倍性能提升,官方 Press Kit 的口径同样是「某些场景最高 3 倍」,skip scan 则没有任何官方数字;pganalyze 在 AWS EBS(2 万 IOPS)上的冷缓存实测里,对 3.5 GB 表全表 COUNT:PG17 约 15.8 秒,PG18 的 worker 模式约 10.1 秒,io_uring 模式约 5.7 秒——worker 与 io_uring 相对同步读稳定落在 2–3 倍区间,云盘等高延迟存储收益最大,本地 NVMe 提升有限。
用法上一条配置决定一切,io_method 取三个值(重启生效):
io_method = worker # 默认值,通用性最好,后台 I/O worker 进程代为预取
io_workers = 3 # 仅 worker 模式生效,默认 3,高 IOPS 存储可调大,可 reload
io_combine_limit = 128kB # 单次合并 I/O 大小,调大要同时改 io_max_combine_limit
选 io_uring 走 Linux 内核的异步队列,开销最小,但要求编译时带 --with-liburing 且内核支持(一般要求 5.1 以上);sync 则退回 17 的行为,等于关掉 AIO。配套还有两处变化:effective_io_concurrency 语义升级为直接控制预读深度,默认值从 1 提到 16,且在缺乏 fadvise 的系统上也能设为大于 0;新增 pg_aios 视图可以观察在途的异步请求。
flowchart LR
BE[后端进程] -->|排队提交读请求| AIO[AIO 层 io_method]
AIO -->|worker| WK[I/O worker 进程]
AIO -->|io_uring| UR[Linux 内核 io_uring]
WK --> SB[(shared buffers)]
UR --> SB
SB --> SCAN[顺序扫描、位图堆扫描与 VACUUM]
两点升级提醒。其一,AIO 目前只覆盖读:顺序扫描、位图堆扫描与 VACUUM 等,写路径仍是同步。其二,可观测性语义变了:worker 模式下后端会出现新的 AIO 等待事件,io_uring 的活动在 pg_stat_activity 里不可见、要用 pg_aios 看;pganalyze 还观察到 EXPLAIN ANALYZE 的 I/O 计时在 AIO 下可能低估实际工作量,容量评估建议以 buffer 计数为主。
小结:AIO 是这版最值得升级的理由,读重的云上实例几乎白拿性能,但要把监控面板与压测口径跟着换一套。
skip scan:复合索引的「跳读」能力
在此之前,B-tree 复合索引的规则近乎铁律:前导列必须有等值条件,因为索引按第一列排序,绕开第一列的约束就只能全索引扫。18 允许优化器对复合 B-tree 索引做 skip scan——当前导列没有等值条件(或只有非等值条件)、而后面的列有可用约束时,扫描器为前导列动态生成等值约束,按每个可能取值「跳着」检索索引,官方文档的例子如下:
CREATE INDEX ON orders (customer_id, status);
-- 18 之前:前导列 customer_id 无条件,只能顺序扫描
-- 18 起:planner 可以对 status = 'paid' 走 skip scan
SELECT * FROM orders WHERE status = 'paid';
它不是免费的午餐:planner 只有在「前导列 distinct 值足够少、期望能跳过大部分叶子页」时才选这条路;前导列基数极大时,skip scan 退化成全索引扫,多数情况不如顺序扫。它还能在多列约束的场景局部生效——例如 WHERE a = 5 AND b >= 42 AND c < 77,扫描会在 a、b 的分组内对 c 跳读。
对升级的实操含义:以前为了这类查询专门「反转」索引列序、或再造一条窄索引的调优套路,18 之后可以先按业务直觉建复合索引,让 planner 自己决定怎么扫。经验法则不变的部分是:尾列等值、前导列低基数的组合收益最大;命中与否由代价模型说了算,上线前后都要拿真实数据 EXPLAIN 验证,不要指望它对所有的「缺前导列」查询都生效。
小结:skip scan 减少「建索引还得伺候查询写法」的心智负担,本质是 planner 在更多索引形态上有了可用计划。
开发者体验:虚拟生成列、uuidv7 与 RETURNING 双值
虚拟生成列:生成列此前只有 STORED 一种——写入时算好、像普通列一样占存储。18 把 VIRTUAL 变成默认:列不占空间,读取时计算,写密集的表可以放心加派生列而不付写放大:
CREATE TABLE people (
height_cm numeric,
height_in numeric GENERATED ALWAYS AS (height_cm / 2.54) -- 默认 virtual
);
限制要记牢:虚拟列的表达式只能用内置函数与内置类型,不能引用用户自定义函数(STORED 列无此限制);逻辑复制新增的 publish_generated_columns 参数目前也只支持 STORED 列。旧版本迁来的 STORED 列语义不变,无升级风险。
uuidv7():随机 UUIDv4 做主键的经典问题是索引局部性差、缓存命中率低、页分裂频繁。RFC 9562 的 UUIDv7 把毫秒级 UNIX 时间戳放进高位、辅以亚毫秒计数与随机位,取值天然按时间有序。18 将其内置,还可选 interval 参数做时间偏移(偏出 48 位时间戳范围会报错),并给随机版本补了显式别名 uuidv4():
SELECT uuidv7(); -- 019535d9-3df7-79fb-b466-fa907fa17f9e
SELECT uuid_extract_timestamp(uuidv7()); -- 从 UUID 里还原生成时间
RETURNING 新旧双值:INSERT/UPDATE/DELETE/MERGE 现在可以在 RETURNING 里同时取改前与改后的值,别名可自定义:
UPDATE accounts SET balance = balance - 100
WHERE id = 42
RETURNING old.balance AS before, new.balance AS after;
审计、行级 diff 这类过去要「先读后写」或塞给触发器的场景,一条语句搞定。另外 18 支持 WITHOUT OVERLAPS 时间约束与 PERIOD 外键,预约不重叠这类校验从应用与触发器下沉为声明式约束。
小结:这一组特性都指向同一件事——把常见应用层仪式收进数据库一条 SQL 里,升级即得,几乎无学习成本。
升级实操:统计保留、--swap 与行为变化
大版本升级最疼的两点是停机窗口和升级后的执行计划抖动,18 在两点上都有实质改善。pg_upgrade 现在默认把旧集群的优化器统计转移进新集群——升级完的计划立刻是「有统计的」。有三类数据不在转移范围:CREATE STATISTICS 建的扩展统计、扩展自定义的统计、累计统计系统(pg_stat 系列表)的数据,升级后补一次即可:
vacuumdb --all --analyze-in-stages --missing-stats-only # 只给缺统计的关系补最小统计
vacuumdb --all --analyze-only # 完整分析,可配 --jobs 并行
传输模式上 18 新增 --swap:直接交换新旧数据目录、再把 catalog 文件替换为新版,官方文档称在关系数多的集群上快于 --link/--clone/--copy。代价是旧目录被破坏性修改,文件传输一旦开始就无法回退启动旧库,务必先有备份并用 --check 完整演练;--jobs 可并行处理多个数据库与表空间,官方建议起步设为 CPU 核数。
社区升级指南里反复出现的拦路虎是扩展兼容:每个已安装扩展都要提供新 major 版本可用的 so 动态库与 UPDATE 脚本,否则 pg_upgrade 的前置检查直接失败;遗留的 fdw/外部服务器等对象也可能让检查环节卡住。等配环境把全部扩展装齐、功能过一遍,是演练不可省的一步。
升级清单上还有几个行为变化要逐条过:
initdb默认开启数据校验和(针对新集群,想关用--no-data-checksums),校验和的写开销要重新纳入容量评估。- 监控面板注意:
pg_stat_io新增read_bytes/write_bytes/extend_bytes并删除op_bytes列;部分 WAL 读写统计从pg_stat_wal迁入pg_stat_io,旧面板会直接断列。 effective_io_concurrency默认值从 1 变 16,升级后预读行为改变,I/O 敏感实例应压测确认。- MD5 口令认证开始输出弃用警告(
md5_password_warnings控制),尽快迁 SCRAM。 autovacuum_max_workers改为运行期可调(配套autovacuum_worker_slots),高峰扩容不再要重启。- 复制槽新增
idle_replication_slot_timeout,闲置槽自动失效,给 WAL 膨胀兜底。
小结:统计保留 + --swap 把升级窗口和升级后调优两头的成本都砍了一截,但监控断列与默认值变化要求升级演练里包含「看板过一遍」。
监控与安全补遗
如果只关心运维,这几项值得单独一提。可观测性:EXPLAIN ANALYZE 默认包含 BUFFERS,扫描节点显示索引查找次数,Materialize/WindowAgg/CTE 节点显示内存与磁盘占用;pg_stat_all_tables 新增 vacuum 与 analyze 的累计耗时四列;pg_stat_get_backend_io() 与 pg_stat_get_backend_wal() 支持按后端排查 I/O 与 WAL;log_connections 细化到连接各阶段并输出耗时,log_line_prefix 新增 %L 输出客户端 IP。安全:OAuth 2.0 认证进入核心(校验经 oauth_validator_libraries 挂载扩展,需 --with-libcurl 编译);TLS 侧 ssl_groups 取代 ssl_ecdh_curve 且默认 X25519,新增 ssl_tls13_ciphers;线路协议升级到 3.2——2003 年(7.4 版)以来首次大版本变更,取消请求密钥升到 256 位。优化器还有一批「白捡」的改写:自连接消除、OR 条件转数组比较、SELECT DISTINCT 键重排、GROUP BY 冗余列删除,都是升级后自动生效。
小结:监控侧的改动密度不亚于性能侧,升级公告值得逐行读完再动生产。
升级建议
截至 2026-10-10,18 已发布约一年,主流扩展兼容性基本就绪。给出如下顺序建议:
- 评估收益排序:云盘/网络存储、大表顺序扫描与 VACUUM 繁重的实例收益最大,优先升;本地 NVMe 且 I/O 不饱和的实例按维护窗口从容安排。
- 预演:
pg_upgrade --check配合--swap在等配机器上完整走一遍,确认旧目录破坏性变更的回退预案,备份永远优先。 - 依赖盘点:逐条确认扩展的新版 so 与 UPDATE 脚本是否齐备,再过虚拟生成列的内置函数限制、
pg_stat_io列变更、MD5 弃用警告、OpenSSL 1.1.1 与 LLVM 14 编译要求。 - 升级后:先跑
vacuumdb --missing-stats-only,用 EXPLAIN 确认既有慢查询是否吃到 skip scan 与优化器改写红利,再把io_method从 worker 试到 io_uring。 - 灰度观察:AIO 下 EXPLAIN 的 I/O 计时偏乐观,观察期以
pg_aios与 buffer 计数为准,数字回归正常后再下结论。
总体判断:18 是一次「底层重写、上层无痛」的升级——异步 I/O 与 skip scan 值得为它专门排一次升级窗口,而统计保留让这次升级比以往任何一版都更省心。
参考资料
- PostgreSQL 18 Release Notes —— 本文全部特性描述的基准来源,逐项核对过。
- PostgreSQL 18 Released!(官方发布公告) —— 「最高 3 倍读性能」等官方口径的出处。
- PostgreSQL 18 文档:资源消耗配置 ——
io_method/io_workers/io_combine_limit的权威定义。 - PostgreSQL 18 文档:多列索引 —— skip scan 的触发条件与官方示例。
- PostgreSQL 18 文档:生成列 —— VIRTUAL/STORED 语义与虚拟列限制。
- PostgreSQL 18 文档:UUID 函数 ——
uuidv7()签名与示例。 - PostgreSQL 18 文档:pg_upgrade —— 统计保留范围、
--swap代价与回退边界。 - pganalyze:Accelerating Disk Reads with Asynchronous I/O —— AIO 架构解析与 EBS 冷缓存实测数据。
读者留言
COMMENTS 暂无还没有留言,来说第一句?