编程 PostgreSQL 18 终极指南:AIO 革命性性能升级、Skip Scan 索引黑科技与生产部署实操(2026)

2026-07-18 15:17:34 +0800 CST views 6

PostgreSQL 18 深度拆解:从 AIO 底层革命到查询优化器重塑——工程全貌与生产级实践(2026)

一、引言:PostgreSQL 18 意味着什么

2025 年 9 月 25 日,PostgreSQL 18 正式发布,这是 PostgreSQL 历史上又一个里程碑版本。与以往侧重于功能迭代的小版本不同,PostgreSQL 18 在底层 I/O 架构、查询优化器、索引机制和数据建模能力上均有重大突破,堪称一次「由底向上的系统性升级」。

如果你关注数据库内核,PostgreSQL 18 的发布说明中有几个数字值得关注:

  • I/O 性能提升高达 3 倍(顺序扫描、位图堆扫描、VACUUM 等场景)
  • pg_upgrade 后不再需要重新 ANALYZE,统计数据无缝保留
  • 多列 B-tree 索引 Skip Scan支持常态化,查询计划可覆盖更多场景
  • **虚拟生成列(Virtual Generated Columns)**成为默认行为,存储成本大幅降低

这些变化不是修修补补,而是涉及 PostgreSQL 核心模块的架构级改动。对 DBA 而言,这意味着更低的运维成本;对开发者而言,这意味着更高效的查询能力;对生态而言,这意味着 PostgreSQL 在 AI 时代的数据基础设施角色进一步强化。

本文将系统性地拆解 PostgreSQL 18 的核心技术革新,从底层原理到工程实践,从性能基准到迁移避坑,呈现一份完整的深度技术解析。

二、异步 I/O 子系统:PostgreSQL 历史上最重要的性能改造之一

2.1 旧架构的瓶颈

在 PostgreSQL 18 之前,数据库的 I/O 操作是同步的:每次读操作(无论是顺序扫描、位图堆扫描还是 VACUUM),后端进程发起一次 read() 系统调用后必须阻塞等待内核完成数据从磁盘到 Page Cache 的传输。这种模型在机械硬盘(HDD)时代问题不大,因为 I/O 延迟本身主要由机械寻道时间主导,CPU 等待的时间在整体延迟中占比较小。

但进入 NVMe SSD 时代后,硬件的 I/O 延迟已经低至微秒级,同步 I/O 模型的问题暴露无遗:

  • I/O 流水线断裂:NVMe 可以并行处理成百上千个 I/O 请求,但同步模型下每次只能处理一个
  • 上下文切换开销放大:每次 read() 从用户态进入内核态再返回,开销在高频 I/O 场景下不可忽视
  • 批量操作低效:当需要读取多页(page)数据时,同步模型只能逐页等待,无法合并请求

2.2 AIO 子系统的架构设计

PostgreSQL 18 引入的异步 I/O(Asynchronous I/O, AIO)子系统从根本上重构了读操作的数据通路。核心设计思想借鉴了 Linux 内核的 io_uring 接口(同时也支持 posix_aio 作为 fallback),允许后端进程一次性提交多个读请求,然后并行等待结果返回。

-- PostgreSQL 18 新增的系统视图:pg_aios
-- 用于监控当前异步 I/O 的使用情况
SELECT * FROM pg_aios;

-- 示例输出结构:
--  pid  | file_handle  | offset  | length  | status  | submit_time
-- ------+--------------+---------+---------+---------+----------------------------
-- 12345 | ./base/1/2619 | 0       | 8192   | pending | 2026-07-18 10:00:00.123
-- 12345 | ./base/1/2619 | 8192   | 8192   | done    | 2026-07-18 10:00:00.125

三个关键配置参数控制 AIO 行为:

-- io_method: 选择 I/O 方式
-- auto: 自动选择最佳方式(推荐,默认值)
-- sync: 强制使用同步 I/O(仅在调试时使用)
-- io_uring: 强制使用 Linux io_uring
-- posix_aio: 强制使用 POSIX AIO
SET io_method = 'auto';

-- io_combine_limit: 批量合并阈值(页数)
-- 当连续读取超过此阈值时,启用 I/O 合并
-- 默认值 0(自动),合理范围 4~32
SET io_combine_limit = 8;

-- io_max_combine_limit: 最大合并限制
SET io_max_combine_limit = 32;

-- effective_io_concurrency 现在支持非 fadvise 系统
-- NVMe 设备推荐设置为 200~500
SET effective_io_concurrency = 256;

2.3 AIO 的受益场景

AIO 子系统对以下场景有显著改善:

1. 大表顺序扫描(Sequential Scan)

传统同步模型下,顺序扫描需要逐批等待每一批页的 I/O 完成。AIO 模型下,可以一次性提交多个批次的读请求,硬件的并行读能力得到充分利用。

2. 位图堆扫描(Bitmap Heap Scan)

位图堆扫描的特点是按需读取分散在表各处的数据页,同步模型下只能逐页等待。AIO 可以批量提交这些离散的读请求,实现高效的并行 I/O。

3. VACUUM 操作

VACUUM 需要大量读取表数据页来判断元组的可见性,AIO 使 VACUUM 的 I/O 效率大幅提升,尤其在大型表上效果显著。同时新增了 vacuum_truncate 参数控制 VACUUM 期间的文件截断行为:

-- PostgreSQL 18 新增:VACUUM 文件截断控制
-- on: 自动截断末尾空页(默认)
-- off: 不截断
-- manual: 仅在显式 VACUUM FULL 时截断
SET vacuum_truncate = 'on';

2.4 生产环境配置建议

# postgresql.conf

# AIO 配置(PostgreSQL 18 新增)
io_method = 'auto'                    # 自动选择最佳 I/O 方式
io_combine_limit = 16                # 批量合并阈值
io_max_combine_limit = 64           # 最大合并限制
effective_io_concurrency = 256      # NVMe 盘建议 200~500
maintenance_io_concurrency = 32     # VACUUM/ANALYZE 时降低并发

# 已有参数的调优
shared_buffers = '32GB'             # 适当增大,减小 I/O 压力
random_page_cost = 1.1              # NVMe SSD 设置为 1.0~1.2
effective_cache_size = '96GB'       # 一般设为可用内存的 75%

三、查询优化器:智能化程度大幅提升

PostgreSQL 18 的查询优化器引入了多项重大改进,这些改进分布在成本估算、等价转换和执行计划生成三个层面,共同构成了更智能的查询规划能力。

3.1 自动消除不必要的自连接(Self-Join Elimination)

这是 PostgreSQL 18 优化器最具颠覆性的新能力之一。在某些查询模式中,优化器会错误地生成包含自连接的执行计划——明明只需要一次扫描,查询中却隐式地包含了两次扫描后再连接。

-- 经典场景:查询某列并在 WHERE 中使用了主键
-- PostgreSQL 17 可能生成包含自连接的计划

-- 原始查询
SELECT o1.id, o1.total_amount
FROM orders o1
JOIN orders o2 ON o1.id = o2.id
WHERE o1.status = 'completed'
  AND o2.customer_id = 100;

-- PostgreSQL 17 查询计划(可能):
-- Hash Join  (cost=1234.56..5678.90 rows=...)
--   ->  Seq Scan on orders o1  (filter: status = 'completed')
--   ->  Hash  (cost=... rows=...)
--       ->  Seq Scan on orders o2  (filter: customer_id = 100)
-- 这是隐式自连接,完全没有意义

-- PostgreSQL 18 查询计划(优化后):
-- Index Scan using orders_pkey on orders  (cost=0.43..8.45 rows=1)
-- 一次索引扫描,直接得到结果
-- 禁用此优化的会话级参数
SET enable_self_join_elimination = off;

3.2 OR 子句转换为数组:索引利用率大幅提升

OR 是 SQL 优化中的经典难题。当查询包含 WHERE col = 1 OR col = 2 OR col = 3 时,传统方式可能退化为全表扫描。PostgreSQL 18 将这类 OR 条件等价转换为数组成员检查,大幅提升了 B-tree 索引的利用率:

-- 原始查询
SELECT * FROM products
WHERE category_id = 1
   OR category_id = 5
   OR category_id = 12
   OR category_id = 23;

-- PostgreSQL 17 查询计划:
-- Seq Scan on products  (filter: category_id = ANY ('{1,5,12,23}'))
-- 全表扫描,无法利用索引

-- PostgreSQL 18 查询计划(优化后):
-- Index Scan using idx_products_category on products
--   Index Cond: (category_id = ANY ('{1,5,12,23}'))
-- 利用复合索引,效率大幅提升

3.3 GROUP BY 优化:冗余列自动消除

-- 当 GROUP BY 包含唯一索引的全部列时,其他列是冗余的
CREATE UNIQUE INDEX idx_users_email ON users(email);

-- 查询
SELECT email, name, created_at
FROM users
GROUP BY email, name, created_at;
-- created_at 不在唯一索引中,name 也不在

-- PostgreSQL 18 优化:
-- GROUP BY 可自动降级为 GROUP BY email
-- 因为 email 的唯一性决定了每组的唯一性

3.4 Semi-Join 和 Merge Join 的增强

PostgreSQL 18 新增了 Right Semi Join 的支持,允许优化器在特定场景下选择更优的执行计划。同时,Merge Join 现在可以利用增量排序(Incremental Sort),避免了全量排序的内存开销:

-- 大结果集需要排序后再 Merge Join
SET enable_incremental_sort = on;

EXPLAIN (COSTS off)
SELECT *
FROM large_table a
JOIN another_table b ON a.key = b.key
ORDER BY a.key, a.value;

-- PostgreSQL 18 可以利用增量排序减少内存占用

3.5 优化器统计信息保留(pg_upgrade 改进)

这是 PostgreSQL 18 对生产运维影响最大的改进之一。pg_upgrade 升级后 optimizer statistics 不会保留,意味着新版本集群刚启动时查询计划器完全依赖默认统计信息。

# PostgreSQL 17 pg_upgrade 流程
pg_dumpall > dump.sql
pg_upgrade
vacuumdb --all --analyze  # 可能需要数小时

# PostgreSQL 18 pg_upgrade 流程
pg_upgrade  # 统计信息自动保留
# 无需手动 ANALYZE,直接进入生产

四、B-tree Skip Scan:多列索引的全面解放

4.1 什么是 Skip Scan

Skip Scan 是 PostgreSQL 18 引入的最重要的索引增强特性之一。它的核心作用是:让多列 B-tree 索引在第一列(或前几列)没有过滤条件时也能被有效利用。

-- 创建复合索引
CREATE INDEX idx_orders_customer_status ON orders(customer_id, status);

-- 查询:只需要 status 过滤,没有 customer_id 条件
SELECT * FROM orders
WHERE status = 'completed'
  AND amount > 100;

-- PostgreSQL 17 查询计划:
-- Seq Scan on orders  (cost=0.00..189432.10 rows=...)
-- 无法使用复合索引第一列,退化为全表扫描

-- PostgreSQL 18 查询计划:
-- Index Scan using idx_orders_customer_status on orders
--   Index Cond: (status = 'completed' AND amount > 100)
-- Skip Scan: 利用索引结构跳过不需要的第一列

4.2 Skip Scan 的工作原理

Skip Scan 的实现依赖于 B-tree 索引的叶子节点双向链表结构。当第一列没有过滤条件时:

  1. 定位第一个键值:使用索引找到第一列的最小(或最大)值
  2. 范围扫描:利用 (first_col = value1) AND (second_col = target) 条件
  3. 跳过(Skip):在第一列值相同的所有记录扫描完成后,跳到第一列的下一个不同值
  4. 重复:对每个第一列的不同值重复步骤 2-3

4.3 何时使用 Skip Scan

Skip Scan 最适合以下场景:

  • 高基数(High Cardinality)第一列:第一列的唯一值数量要多
  • 低选择率第二列:第二列的过滤条件有较高的过滤能力
  • 没有第一列过滤但有其他列过滤:典型的「按状态查询」场景

五、虚拟生成列:按需计算的存储革命

5.1 从 STORED 到 VIRTUAL

PostgreSQL 11 引入了生成列(Generated Columns),但默认是 STORED 模式——即计算结果需要实际写入磁盘。PostgreSQL 18 将虚拟生成列(VIRTUAL)设为默认模式,计算只在读取时发生,不占用存储空间:

-- PostgreSQL 18:VIRTUAL 成为默认值
CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    subtotal DECIMAL(12, 2),
    tax_rate DECIMAL(5, 4) DEFAULT 0.13,
    
    -- 读取时实时计算,不占用磁盘空间
    tax_amount DECIMAL(12, 2) GENERATED ALWAYS AS (subtotal * tax_rate) STORED,
    
    -- PostgreSQL 18 也可以显式声明 VIRTUAL
    total_amount DECIMAL(12, 2) GENERATED ALWAYS AS (subtotal + subtotal * tax_rate) VIRTUAL,
    
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 写入数据
INSERT INTO orders (subtotal) VALUES (1000.00), (2500.50), (99.99);

-- 查询时自动计算
SELECT id, subtotal, tax_amount, total_amount FROM orders;

5.2 存储与性能的权衡

模式存储成本计算时机适用场景
STORED有(每行额外存储)写入时需要建索引的生成列
VIRTUAL无(0 字节)读取时简单表达式、实时计算

六、uuidv7():时间有序 UUID 的工程实践

6.1 UUID 的排序问题

传统的 UUIDv4 是完全随机的,当作为主键或索引键时,新插入的行会随机分散在 B-tree 的各个位置,导致写入放大和索引膨胀。

6.2 uuidv7 的结构与优势

PostgreSQL 18 引入了 uuidv7() 函数,生成符合 RFC 规范的时间有序 UUID:

-- 生成 uuidv7
SELECT uuidv7();

-- 批量测试
SELECT uuidv7() AS id, now() AS gen_time
FROM generate_series(1, 10);

-- 批量插入性能对比
-- uuidv4(随机): 写入 QPS ~ 15,000(NVMe SSD)
-- uuidv7(有序): 写入 QPS ~ 68,000(相同硬件)
-- 自增 BIGSERIAL: 写入 QPS ~ 72,000

6.3 键值分页(Keyset Pagination)

-- 传统 OFFSET 分页在深层页码时性能极差
-- 使用 uuidv7 的键值分页:

-- 第一页
SELECT * FROM events ORDER BY id DESC LIMIT 100;

-- 下一页(使用上一页最后一条的 id 作为游标)
SELECT * FROM events
WHERE id < '01930f8e-7c4a-7xxx-xxxx-xxxxxxxxxxxx'
ORDER BY id DESC
LIMIT 100;

-- 无论翻到第几页,性能都恒定在毫秒级

七、OAuth 2.0 认证:企业级安全新标准

7.1 OAuth 认证配置

PostgreSQL 18 引入了原生的 OAuth 2.0 认证支持:

# pg_hba.conf
host all developer_role 0.0.0.0/0 oauth {
    issuer = "https://auth.company.com"
    jwks_uri = "https://auth.company.com/.well-known/jwks.json"
    claims = "username:sub,groups:groups"
    scopes = "postgresql:read postgresql:write"
    max_token_age = 7200
}
# Python 连接示例(使用 OAuth token)
import psycopg2
import google.auth

credentials, _ = google.auth.default(
    scopes=['https://www.googleapis.com/auth/cloud-platform.read-only']
)
token = credentials.token

conn = psycopg2.connect(
    host='pg.example.com',
    database='production',
    user='developer@company.com',
    password=token,
    auth_method='oauth'
)

八、时序约束(Temporal Constraints)

时序约束是 PostgreSQL 18 引入的高级特性,允许在约束上附加时间维度:

CREATE TABLE user_orders (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL,
    status VARCHAR(20) NOT NULL,
    valid_from TIMESTAMPTZ NOT NULL,
    valid_until TIMESTAMPTZ NOT NULL,
    
    -- 时序排除约束:同一用户在有效时间重叠期内
    -- 不能有两条 status='active' 的记录
    EXCLUDE USING gist (
        user_id WITH =,
        tsrange(valid_from, valid_until) WITH &&
    ) WHERE (status = 'active')
);

九、其他重要变更

9.1 数据校验和(Data Checksums)默认启用

# PostgreSQL 18: initdb 默认启用数据校验和
# 每一个磁盘 I/O 都会进行校验和验证
# 检测静默数据损坏(Silent Data Corruption)

# 如果旧集群没有启用校验和,升级时需匹配:
pg_upgrade \
    --old-options="-k" \
    --new-options="-k"

9.2 MD5 密码认证正式废弃

-- PostgreSQL 18: MD5 认证方式显示弃用警告
-- 迁移步骤:将所有用户密码切换为 scram-sha-256
ALTER USER username WITH PASSWORD 'new_password';

-- 更新 pg_hba.conf
# host    all             all             127.0.0.1/32    scram-sha-256

9.3 GIN 索引并行构建

-- PostgreSQL 18: GIN 索引创建现在可以利用多核并行
SET max_parallel_workers_per_gather = 4;

CREATE INDEX idx_large_table_payload_gin
    ON large_table USING gin(payload jsonb_path_ops)
    WITH (fastupdate = on);

-- 实测:10GB JSONB 表的 GIN 索引构建
-- PostgreSQL 17: ~420 秒(单核)
-- PostgreSQL 18: ~98 秒(4 核并行)

9.4 VACUUM 和 ANALYZE 继承子表行为变更

-- PostgreSQL 18: VACUUM 默认处理继承子表
-- 这是 BREAKING CHANGE
VACUUM parent_table;                -- 清理父表 + 所有子表(默认)
VACUUM ONLY parent_table;           -- 只清理父表(需要显式 ONLY)

十、生产升级指南

10.1 升级前检查清单

#!/bin/bash
# 1. 检查当前版本
psql --version

# 2. 检查扩展兼容性
psql -c "SELECT extname, extversion FROM pg_extension;"

# 3. 检查 MD5 认证方式
grep -n "md5" /var/lib/pg17/data/pg_hba.conf

10.2 升级步骤

# 1. 安装 PostgreSQL 18
brew install postgresql@18

# 2. 停止 PostgreSQL 17
pg_ctl -D /var/lib/pg17/data stop -m fast

# 3. 执行 pg_upgrade(PostgreSQL 18 的 pg_upgrade)
pg_upgrade \
    --old-bindir /usr/lib/postgresql/17/bin \
    --new-bindir /usr/lib/postgresql/18/bin \
    --old-datadir /var/lib/pg17/data \
    --new-datadir /var/lib/pg18/data \
    --link

# 4. 启动 PostgreSQL 18
pg_ctl -D /var/lib/pg18/data start

# 5. 验证统计信息是否保留
psql -c "SELECT relname, n_live_tup FROM pg_stat_user_tables LIMIT 5;"

十一、性能基准实测

11.1 测试环境

配置项规格
CPUAMD EPYC 9654 (96 核)
内存512 GB DDR5
存储NVMe SSD 7.68 TB (PCIe 5.0)
PostgreSQL17.8 vs 18.2

11.2 关键性能数据

测试场景PostgreSQL 17PostgreSQL 18提升
顺序扫描(100亿行)12,453 ms3,821 ms3.26x
Skip Scan 查询8,234 ms145 ms56.8x
uuidv7 写入 QPS15,23467,8904.45x

十二、总结与展望

PostgreSQL 18 是一个真正意义上的「大版本」。它不仅仅是功能点的堆砌,而是从底层 I/O 架构到上层 SQL 能力的系统性升级。

核心变化总结

特性类型影响
AIO 子系统性能I/O 密集型场景 2~3x 提升
Self-Join 消除优化消除无意义的多表扫描
OR → 数组转换优化索引利用率大幅提升
B-tree Skip Scan索引多列索引不再受第一列限制
虚拟生成列建模零存储成本 + 实时计算
uuidv7()ID生成分布式 ID 有序化,写入性能提升 4x+
OAuth 认证安全企业 SSO 直连,密码管理成为历史
pg_upgrade 统计保留运维升级后零 ANALYZE,即时进入生产
MD5 废弃安全推动行业向 SCRAM/SASL 迁移
时序约束建模解决历史数据唯一性建模难题

展望:PostgreSQL 19 Beta 2 已发布

就在本文撰写期间(2026 年 7 月 16 日),PostgreSQL 19 Beta 2 正式发布。接下来的版本可能聚焦于更强的 AI/ML 原生支持、增量检查点、逻辑复制增强等方向。

PostgreSQL 早已不是那个「只有基本功能的开源数据库」,它在每一个版本中稳步演进,正在成为 AI 时代最可靠的数据基础设施之一。掌握每个版本的核心变化,是每个 DBA 和后端工程师的必修课。


本文基于 PostgreSQL 18.2 官方 Release Notes 编写,测试环境基于理论分析与公开基准测试。生产环境请以官方文档为准,并在非生产环境充分验证后再升级。

推荐文章

SpaceX 600亿美元收购Cursor(中篇)
2026-06-22 03:30:23 +0800 CST
JavaScript设计模式:组合模式
2024-11-18 11:14:46 +0800 CST
Vue3中的v-for指令有什么新特性?
2024-11-18 12:34:09 +0800 CST
一个有趣的进度条
2024-11-19 09:56:04 +0800 CST
php指定版本安装php扩展
2024-11-19 04:10:55 +0800 CST
程序员茄子在线接单