PostgreSQL 19 Beta 1 深度解析:当「全球最强开源数据库」学会了图查询——从 SQL/PGQ 标准实现到生产级性能优化的完全指南(2026)
引言:PostgreSQL 19 来了,这次真的不一样
2026 年 6 月 4 日,PostgreSQL 全球开发组正式发布了 PostgreSQL 19 Beta 1。作为全球最先进的开源关系型数据库,PostgreSQL 的每一次大版本更新都牵动着无数 DBA 和开发者的神经。而 PG19 这次带来的,绝不仅仅是一堆增量修补——SQL/PGQ 图查询标准的首个完整实现,意味着 PostgreSQL 在不改变存储引擎的前提下,正式进入了图计算领域。
但这远不是 PG19 的全部。从 SIMD 加速的 COPY 导入、自动并行 vacuum、在线启用数据校验和、到异步 I/O 读取调度优化,PG19 在性能层面的改进几乎触及了数据库的每一个核心路径。
本文将从以下维度全面解析 PostgreSQL 19 Beta 1:
- SQL/PGQ 图查询——ISO 标准实现,用 SQL 写图查询,告别 Neo4j
- 查询优化器革命——反连接优化、半连接改进、聚合下推
- 性能全面提速——SIMD、异步 I/O、并行 vacuum、Radix Sort
- 存储与压缩——默认 LZ4 压缩、TOAST 改进
- 运维与可观测性——在线校验和、锁定统计、自动 vacuum 评分系统
- 兼容性变更与迁移指南——破坏性改动一览
一、SQL/PGQ:在关系表上做图查询,这不是玩笑
1.1 什么是 SQL/PGQ?
SQL/PGQ(Property Graph Queries)是 ISO/IEC 9075-16:2023 标准的一部分,它定义了一种在 SQL 中嵌入图模式匹配语法的方式。简单来说,你可以在已有的关系表上声明一个「属性图」,然后用类似 Cypher 的 MATCH 语法来查询它。
在 PG19 之前,如果你想在关系数据库中做图查询,你需要:
- 写出极其复杂的多表自连接(Self-Join)
- 用递归 CTE(Common Table Expressions)来做路径搜索
- 结果 SQL 又臭又长,维护成本极高
而 SQL/PGQ 让这一切变得优雅。
1.2 核心概念:属性图
属性图由两部分组成:
- 顶点(Vertex):代表一个实体,如用户、产品、账户
- 边(Edge):代表实体之间的关系,如好友、购买、关注
两者都可以携带属性。例如一个 Person 顶点可以有 name、birth_year 属性,一条 Knows 边可以有 since(认识时间)属性。
1.3 声明属性图
PG19 引入了 CREATE PROPERTY GRAPH 语法,将现有关系表映射为图的顶点和边:
-- 假设我们有两个表:people 和 friendships
-- people 表(顶点)
CREATE TABLE people (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
birth_year INT,
email TEXT UNIQUE
);
-- friendships 表(边)
CREATE TABLE friendships (
person_a INT REFERENCES people(id),
person_b INT REFERENCES people(id),
since DATE,
closeness NUMERIC(1,1) CHECK (closeness >= 0 AND closeness <= 1)
);
-- 声明属性图
CREATE PROPERTY GRAPH social_graph
VERTEX TABLES (
people LABEL Person PROPERTIES (id, name, birth_year, email)
)
EDGE TABLES (
friendships
SOURCE KEY (person_a) REFERENCES people (id)
DESTINATION KEY (person_b) REFERENCES people (id)
LABEL Knows PROPERTIES (since, closeness)
);
注意几个关键点:
LABEL给顶点/边起了一个语义名称,后续 MATCH 语法中使用PROPERTIES指定哪些列作为图的属性暴露出来SOURCE KEY和DESTINATION KEY定义了边的方向性
1.4 图模式匹配查询
声明完属性图后,你就可以用 GRAPH_TABLE 函数做图查询了:
-- 基础查询:找 Jan 的所有朋友
SELECT friend_name
FROM GRAPH_TABLE (
social_graph
MATCH (a:Person WHERE a.name = 'Jan')-[k:Knows]->(b:Person)
COLUMNS (b.name AS friend_name)
);
-- 两跳查询:找 Jan 的朋友的朋友(朋友的朋友)
SELECT friend_of_friend
FROM GRAPH_TABLE (
social_graph
MATCH (a:Person WHERE a.name = 'Jan')-[k1:Knows]->(b:Person)-[k2:Knows]->(c:Person)
COLUMNS (c.name AS friend_of_friend)
);
-- 带属性过滤:找 Jan 在 2024 年以后认识的朋友
SELECT friend_name, since_date
FROM GRAPH_TABLE (
social_graph
MATCH (a:Person WHERE a.name = 'Jan')-[k:Knows WHERE k.since > '2024-01-01']->(b:Person)
COLUMNS (b.name AS friend_name, k.since AS since_date)
);
-- 无方向边:双向朋友关系
SELECT mutual_friend
FROM GRAPH_TABLE (
social_graph
MATCH (a:Person WHERE a.name = 'Jan')-[k:Knows]-(b:Person)
COLUMNS (b.name AS mutual_friend)
);
MATCH 子句的语法解析:
(a:Person)—— 匹配一个标签为Person的顶点,绑定到变量aWHERE a.name = 'Jan'—— 顶点过滤器-[k:Knows]->—— 匹配一条标签为Knows的出边-[k:Knows]-(b:Person)—— 无方向匹配(匹配入边或出边)COLUMNS (...)—— 投影你想要的输出列
1.5 实战场景:社交网络推荐系统
让我们构建一个更完整的例子——社交网络的好友推荐引擎:
-- 创建更丰富的数据模型
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
display_name TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
is_active BOOLEAN DEFAULT true
);
CREATE TABLE follows (
follower_id BIGINT REFERENCES users(id),
following_id BIGINT REFERENCES users(id),
created_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (follower_id, following_id)
);
CREATE TABLE user_interests (
user_id BIGINT REFERENCES users(id),
interest_tag TEXT NOT NULL,
weight NUMERIC(1,3) DEFAULT 1.0,
PRIMARY KEY (user_id, interest_tag)
);
-- 声明属性图
CREATE PROPERTY GRAPH recommendation_graph
VERTEX TABLES (
users LABEL User PROPERTIES (id, username, display_name, created_at, is_active)
)
EDGE TABLES (
follows
SOURCE KEY (follower_id) REFERENCES users (id)
DESTINATION KEY (following_id) REFERENCES users (id)
LABEL Follows PROPERTIES (created_at)
);
-- 推荐「关注的人也关注的人」
SELECT DISTINCT recommended
FROM GRAPH_TABLE (
recommendation_graph
MATCH (me:User WHERE me.username = 'current_user')
-[f1:Follows]->(my_following:User)
-[f2:Follows]->(fof:User)
WHERE fof <> me
COLUMNS (fof.username AS recommended)
)
ORDER BY recommended
LIMIT 20;
-- 与 common interest 的用户建立连接
SELECT candidate
FROM GRAPH_TABLE (
recommendation_graph
MATCH (me:User WHERE me.username = 'current_user')
-[f:Follows]->(mutual_follow:User)
<-[f2:Follows]-(candidate:User)
WHERE candidate <> me
COLUMNS (candidate.username AS candidate)
)
ORDER BY candidate
LIMIT 10;
1.6 底层实现原理:重写器,而非图引擎
这里有一个非常重要的设计决策需要理解:PG19 的 SQL/PGQ 实现不是一个图存储引擎,而是一个查询重写器。
当你执行 GRAPH_TABLE 查询时,PG19 内部的处理流程如下:
GRAPH_TABLE 查询
↓
图模式解析器(解析 MATCH 语法)
↓
图重写器(将图模式转换为关系代数)
↓
标准查询规划器(已有的优化器)
↓
执行器(已有的执行引擎)
MATCH (a:Person)-[k:Knows]->(b:Person) 实际上被重写为:
-- 概念上等价于:
SELECT b.*
FROM people a
JOIN friendships k ON k.person_a = a.id
JOIN people b ON b.id = k.person_b
WHERE a.name = 'Jan';
这个设计带来了两个重大影响:
第一:你现有的索引全部有效。
因为重写器生成的是标准的关系连接(JOIN),所以你在 friendships(person_a) 上建的 B-tree 索引会被正常使用。不需要维护独立的图索引,不需要额外的存储开销。
第二:性能特征与关系查询一致。
图遍历变成 JOIN 链。对于浅层、固定深度的查询(1-3 跳),性能非常优秀——因为 PG 本来就擅长索引连接。但对于深层、可变长度的遍历(如六度分隔),性能可能不如专用图数据库如 Neo4j。
1.7 PG19 的 SQL/PGQ 局限性
作为首个实现,PG19 的 SQL/PGQ 有一些需要注意的限制:
- 不支持可变长度路径:
-[k:Knows*1..5]->(最多 5 跳)尚未支持,这是后续版本的重点 - 不支持量化模式:如
MATCH SHORTEST、ALL SHORTEST等路径算法 - 性能深度有限:对于超过 3 跳的深层遍历,性能特征等同于等价的多表 JOIN
对于大多数业务场景(2-3 跳的朋友查询、权限路径审计、数据血缘追踪),PG19 的 SQL/PGQ 已经完全够用了。
二、查询优化器的全面升级
PG19 在查询优化器方面做了大量改进,有些改动直接影响你写的每一行 SQL。
2.1 NOT IN → 反连接优化
NOT IN 子查询一直是 PostgreSQL 性能优化的老大难问题。当子查询中存在 NULL 值时,NOT IN 无法被优化为反连接(Anti-Join),导致全表扫描。
PG19 中,优化器现在能够在检测到 NULL 不存在时,将 NOT IN 转换为更高效的 ANTI JOIN:
-- 优化前:如果 status 中有 NULL,会退化为全表扫描
SELECT * FROM orders
WHERE customer_id NOT IN (
SELECT id FROM customers WHERE status = 'inactive'
);
-- PG19 优化后:当优化器能证明 NULL 不存在时
-- 自动转换为 Anti-Join,走索引
EXPLAIN ANALYZE
SELECT * FROM orders o
WHERE NOT EXISTS (
SELECT 1 FROM customers c
WHERE c.id = o.customer_id AND c.status = 'inactive'
);
2.2 更多 LEFT JOIN → Anti-Join 转换
PG19 扩大了 LEFT JOIN 到 ANTI JOIN 的转换范围。当外连接的结果不包含 NULL 行(即无法匹配的情况没有副作用),优化器可以将 LEFT JOIN 替换为 ANTI JOIN,大幅减少中间结果集。
-- 经典场景:查找没有下过订单的客户
SELECT c.name, c.email
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;
-- PG19 可能将其转换为:
SELECT c.name, c.email
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);
2.3 Memoize 用于 Anti-Join
Memoize 是 PostgreSQL 的一个内置操作符,用于缓存子查询的结果。PG19 现在允许 Memoize 用于内侧唯一的反连接,这在关联子查询场景下可以显著减少重复计算。
2.4 聚合下推到 JOIN 之前
这是一个非常有深度的优化。某些聚合操作现在可以在 JOIN 之前执行,从而减少 JOIN 需要处理的行数:
-- 示例:查找每个部门工资最高的人
-- PG19 可能在 JOIN 前先完成 GROUP BY,减少 JOIN 数据量
SELECT d.dept_name, e.name, e.salary
FROM (
SELECT dept_id, MAX(salary) AS max_salary
FROM employees
GROUP BY dept_id
) dept_max
JOIN employees e ON e.dept_id = dept_max.dept_id AND e.salary = dept_max.max_salary
JOIN departments d ON d.id = e.dept_id;
2.5 其他优化器改进
| 优化项 | 说明 |
|---|---|
IS NOT DISTINCT FROM NULL → IS NOT NULL | 常量折叠简化 |
Var IS NULL 早期评估 | 让下游优化器利用更多信息 |
| Append/MergeAppend 支持增量排序 | 分区表查询受益 |
| Hash Join NULL 键处理改进 | 避免不必要的数据倾斜 |
| 半连接(Semi-Join)规划改进 | EXISTS 子查询更快 |
| 扩展统计支持虚拟生成列 | 对生成列的统计更准确 |
pg_restore_extended_stats() | 扩展统计终于可以备份恢复了 |
三、性能:从 SIMD 到并行 Vacuum 的全面提速
3.1 SIMD 加速 COPY FROM
COPY FROM 是 PostgreSQL 批量导入数据的首选方式。PG19 使用 SIMD(Single Instruction Multiple Data)CPU 指令加速了文本和 CSV 格式的解析。
实测中,对于大字段较多的宽表,COPY FROM 的性能提升可达 20-40%:
-- 传统导入
COPY large_table FROM '/data/import.csv' WITH (FORMAT csv, HEADER true);
-- PG19 中,同样的命令自动使用 SIMD 加速
-- 无需修改任何代码
这对 ETL 流水线和数据迁移场景是实打实的性能红利。
3.2 异步 I/O 读取调度优化
PG19 改进了异步 I/O 的读取预读调度策略,特别是在处理大请求时的表现。新引入的 io_method 配置参数允许 I/O 工作进程自动管理:
-- postgresql.conf 中的新配置
io_min_workers = 2 -- 最小 I/O 工作线程
io_max_workers = 8 -- 最大 I/O 工作线程
io_worker_idle_timeout = 60 -- 空闲超时(秒)
io_worker_launch_interval = 10 -- 工作线程启动间隔(毫秒)
这对 I/O 密集型工作负载(如大表扫描、备份恢复)有显著帮助。
3.3 并行 Vacuum
Autovacuum 终于支持并行工作线程了。在 PG18 及之前,VACUUM 操作是单线程的,对于大表的清理操作可能需要很长时间。
-- 全局配置
autovacuum_max_parallel_workers = 2;
-- 按表配置
ALTER TABLE large_table SET (autovacuum_parallel_workers = 4);
-- 手动并行 vacuum
VACUUM (PARALLEL 4) large_table;
配合 TID Range Scan 的并行化支持,大表的维护操作效率大幅提升。
3.4 Radix Sort(基数排序)
PG19 在排序操作中引入了 Radix Sort(基数排序)算法。Radix Sort 的时间复杂度是 O(n×k),其中 k 是键的最大位数,对于整数排序特别高效。
-- 整数排序自动使用 Radix Sort
SELECT * FROM events ORDER BY id;
-- 字符串排序也可能受益(取决于数据分布)
SELECT * FROM users ORDER BY created_at;
3.5 默认压缩算法改为 LZ4
PG19 将 TOAST(The Oversized-Attribute Storage Technique)的默认压缩算法从 pglz 改为 lz4。LZ4 在压缩速度和解压速度上都显著优于 pglz,虽然压缩比可能略低,但综合性能更好。
-- 查看当前压缩设置
SHOW default_toast_compression;
-- 手动设置
SET default_toast_compression = 'lz4';
-- 创建表时指定
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT COMPRESSION lz4
);
3.6 NOTIFY 性能优化
NOTIFY 命令现在只会唤醒正在监听特定通道的后端进程,而不是唤醒所有后端进程。对于使用 LISTEN/NOTIFY 做实时通知的应用(如 WebSocket 推送),在高并发场景下性能提升显著。
-- 发送端
NOTIFY 'order_updates', '新订单 #' || NEW.id;
-- 接收端(仅相关监听者被唤醒)
LISTEN order_updates;
3.7 其他性能改进
| 改进项 | 影响 |
|---|---|
| 外键约束检查性能提升 | 高并发写入场景受益 |
| 表扫描标记 all-visible 页面 | 减少后续 VACUUM 的工作量 |
| UTF-8 大小写折叠加速 | LC_COLLATE 相关操作更快 |
| Hash 索引批量删除优化 | 大表维护效率提升 |
| GIN 索引 VACUUM 流式读取 | 并行 vacuum 场景更高效 |
| 行解变形(Row Deformation)优化 | 内部数据格式转换更快 |
| JIT 默认关闭 | 减少 OLTP 场景下的 JIT 编译开销 |
四、存储与压缩:LZ4 成为默认选择
4.1 TOAST 压缩机制回顾
PostgreSQL 使用 TOAST 机制存储超过页面大小(通常 8KB)的大字段。当一行数据无法放入单个数据页时,PG 会将大字段压缩并存储到 TOAST 表中。
压缩算法的选择直接影响写入和读取性能:
| 算法 | 压缩速度 | 解压速度 | 压缩比 |
|---|---|---|---|
| pglz(PG 传统) | 中等 | 中等 | 较高 |
| lz4(PG19 默认) | 极快 | 极快 | 中等 |
| zstd(可选扩展) | 慢 | 快 | 最高 |
4.2 实际性能对比
-- 测试不同压缩算法的效果
CREATE TABLE toast_test_pglz (
id SERIAL,
data TEXT COMPRESSION pglz
);
CREATE TABLE toast_test_lz4 (
id SERIAL,
data TEXT COMPRESSION lz4
);
-- 插入 100 万条测试数据
INSERT INTO toast_test_pglz (data)
SELECT repeat(md5(random()::text), 100) FROM generate_series(1, 1000000);
INSERT INTO toast_test_lz4 (data)
SELECT repeat(md5(random()::text), 100) FROM generate_series(1, 1000000);
-- 查看表大小差异
SELECT pg_size_pretty(pg_table_size('toast_test_pglz')) AS pglz_size,
pg_size_pretty(pg_table_size('toast_test_lz4')) AS lz4_size;
对于大多数 OLTP 场景,LZ4 是更好的选择——因为压缩和解压速度更快,CPU 开销更低,而压缩比的差异通常可以接受。
4.3 迁移建议
升级到 PG19 后,现有数据的压缩格式不会自动改变。只有新写入的数据会使用 LZ4。如果你想让现有数据也使用 LZ4:
-- 方法 1:逐表重写(需要停机)
VACUUM FULL table_name;
-- 方法 2:使用逻辑复制
-- 在新集群上重建,数据会自动使用 LZ4 压缩
五、运维与可观测性:DBA 的福音
5.1 在线启用/禁用数据校验和
这是 PG19 最实用的运维特性之一。在 PG18 及之前,数据校验和只能在初始化时设置,修改需要停机并使用 pg_checksums 工具。
PG19 终于支持在线操作:
-- 在线启用数据校验和(无需停机!)
ALTER SYSTEM SET data_checksums = on;
SELECT pg_catalog.pg_enable_data_checksums();
-- 在线禁用
ALTER SYSTEM SET data_checksums = off;
SELECT pg_catalog.pg_disable_data_checksums();
数据校验和能够检测静默数据损坏(如磁盘位翻转),对于金融、医疗等对数据完整性要求极高的场景至关重要。
5.2 自动 Vacuum 评分系统
PG19 引入了一个新的评分系统来控制自动 vacuum 的调度优先级:
-- 新的配置参数
autovacuum_freeze_score_weight = 10.0
autovacuum_multixact_freeze_score_weight = 10.0
autovacuum_vacuum_score_weight = 5.0
autovacuum_analyze_score_weight = 3.0
autovacuum_insert_score_weight = 2.0
-- 监控自动 vacuum 评分
SELECT * FROM pg_stat_autovacuum_scores;
这套评分系统让你能更精细地控制哪些表优先被 vacuum 处理,而不是依赖简单的阈值触发。
5.3 锁定统计视图
新增的 pg_stat_lock 视图提供了按锁类型分类的统计信息:
-- 查看锁定统计
SELECT locktype, mode, granted, count, wait_time
FROM pg_stat_lock
ORDER BY wait_time DESC;
这对排查锁竞争问题非常有用——你可以快速定位哪些锁类型导致了最多的等待。
5.4 恢复状态监控
-- 新视图:查看恢复状态
SELECT * FROM pg_stat_recovery;
对于使用流复制或 PITR 的环境,这个视图提供了恢复进度的详细信息。
5.5 其他监控改进
-- WAL 接收器状态增强(新增 connecting 状态)
SELECT status, sender_host, sender_port FROM pg_stat_wal_receiver;
-- Full Page Write 字节数统计
SELECT wal_fpw_records, wal_fpw_bytes FROM pg_stat_wal;
-- 序列页面 LSN 追踪
SELECT pg_get_sequence_data('my_sequence');
-- MultiXact 统计
SELECT * FROM pg_get_multixact_stats();
-- 动态共享内存详情
SELECT * FROM pg_dsm_registry_allocations;
-- 扩展统计备份恢复
SELECT pg_restore_extended_stats('my_stats_obj');
5.6 日志增强
-- 按进程类型设置不同日志级别
log_min_messages = 'autovacuum:warning, bgwriter:debug5'
-- 记录长时间自动分析操作
log_autoanalyze_min_duration = 1000 -- 毫秒
-- 锁等待日志默认启用
log_lock_waits = on -- 默认已开启!
-- 裸解析树日志(调试用)
debug_print_parse = on
六、兼容性变更与迁移指南
PG19 有一些破坏性变更,升级前务必检查。
6.1 关键破坏性变更
6.1.1 RADIUS 认证被移除
PostgreSQL 只支持基于 UDP 的 RADIUS,而基于 UDP 的 RADIUS 被认为是不安全的。如果你的集群使用 RADIUS 认证,升级前需要切换到其他认证方式(如 LDAP、GSSAPI、SCRAM-SHA-256)。
6.1.2 standard_conforming_strings 强制开启
-- PG18 及以下:可以关闭(允许 E'...' 转义)
SET standard_conforming_strings = off;
-- PG19:强制开启,无法关闭
-- 如果你依赖旧式转义字符串,需要修改应用代码
escape_string_warning 配置参数也被移除。
6.1.3 inet/cidr 默认操作类变更
inet 和 cidr 类型的默认索引操作类从 B-tree 改为 GiST。如果你的旧集群使用了 btree_gist 扩展的 inet/cidr 操作类,pg_upgrade 会失败。
-- 升级前检查
SELECT indexname, indexdef
FROM pg_indexes
WHERE indexdef LIKE '%btree_gist%' AND indexdef LIKE '%inet%';
-- 如果有匹配结果,需要先删除这些索引
DROP INDEX old_inet_index;
-- 然后使用标准 GiST 索引重建
CREATE INDEX new_inet_index ON table USING gist (inet_column);
6.1.4 max_locks_per_transaction 默认值从 64 改为 128
锁空间分配机制发生了变化,实际容量需要翻倍才能匹配之前的容量。对于大部分场景,默认值是足够的,但如果你自定义了这个参数,需要注意调整。
6.1.5 JIT 默认关闭
PG19 将 JIT 编译默认关闭,因为之前的成本估算被认为不够可靠。如果你依赖 JIT 做复杂分析查询,需要手动启用:
SET jit = on;
6.1.6 MULE_INTERNAL 编码被移除
这个编码复杂且几乎不被使用。如果你的数据库使用了这个编码,需要先导出并转换为其他编码。
6.1.7 其他变更
- 数据库名、角色名、表空间名中禁止使用回车符和换行符
json_array()无行时返回空数组(而非 NULL)COPY FROM ... WHERE中禁用系统列CREATE SCHEMA中不再自动重排序非 schema 对象- MD5 密码认证成功后会产生警告(PG18 已弃用 MD5 密码)
- 新增
password_expiration_warning_threshold(默认 7 天)
6.2 升级检查清单
# 1. 检查 RADIUS 认证使用
grep -r "radius" $PGDATA/pg_hba.conf
# 2. 检查 btree_gist inet/cidr 索引
psql -c "SELECT indexname FROM pg_indexes WHERE indexdef ~ 'btree_gist' AND indexdef ~ 'inet'"
# 3. 检查非标准字符串使用
psql -c "SELECT datname, datcollate FROM pg_database WHERE NOT datistemplate"
# 4. 检查 MULE_INTERNAL 编码
psql -c "SELECT datname, encoding FROM pg_database WHERE encoding = -9"
# 5. 使用 pg_upgrade 进行测试升级
pg_upgrade --old-bindir /usr/lib/postgresql/18/bin \
--new-bindir /usr/lib/postgresql/19/bin \
--old-datadir /var/lib/postgresql/18/main \
--new-datadir /var/lib/postgresql/19/main \
--check
七、实战演练:搭建 PG19 并测试 SQL/PGQ
7.1 安装 PG19 Beta 1
# macOS (Homebrew)
brew install postgresql@19-beta
# 或者从源码编译
wget https://ftp.postgresql.org/pub/source/v19beta1/postgresql-19beta1.tar.gz
tar xzf postgresql-19beta1.tar.gz
cd postgresql-19beta1
./configure --prefix=/usr/local/pgsql-19 \
--with-icu \
--with-lz4 \
--with-openssl
make -j$(nproc)
make install
# 初始化数据库
initdb -D /usr/local/pgsql-19/data
# 启动
pg_ctl -D /usr/local/pgsql-19/data -l logfile start
# 启用 LZ4 压缩(postgresql.conf)
echo "default_toast_compression = 'lz4'" >> $PGDATA/postgresql.conf
echo "log_lock_waits = on" >> $PGDATA/postgresql.conf
7.2 完整的 SQL/PGQ 实战案例
-- ========================================
-- 案例企业级系统:供应链追踪
-- ========================================
-- 供应商表(顶点)
CREATE TABLE suppliers (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
country TEXT NOT NULL,
rating NUMERIC(1,1)
);
-- 产品表(顶点)
CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
category TEXT NOT NULL,
unit_price NUMERIC(10,2)
);
-- 仓库表(顶点)
CREATE TABLE warehouses (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
location TEXT NOT NULL,
capacity INT
);
-- 供货关系(边:供应商 → 产品)
CREATE TABLE supplies (
supplier_id BIGINT REFERENCES suppliers(id),
product_id BIGINT REFERENCES products(id),
unit_cost NUMERIC(10,2),
lead_days INT,
PRIMARY KEY (supplier_id, product_id)
);
-- 库存关系(边:产品 → 仓库)
CREATE TABLE stocked_in (
product_id BIGINT REFERENCES products(id),
warehouse_id BIGINT REFERENCES warehouses(id),
quantity INT NOT NULL DEFAULT 0,
reorder_level INT DEFAULT 100,
PRIMARY KEY (product_id, warehouse_id)
);
-- 订单关系(边:仓库 → 仓库,表示调拨)
CREATE TABLE transfers (
from_warehouse_id BIGINT REFERENCES warehouses(id),
to_warehouse_id BIGINT REFERENCES warehouses(id),
product_id BIGINT REFERENCES products(id),
quantity INT NOT NULL,
transfer_date DATE DEFAULT CURRENT_DATE,
PRIMARY KEY (from_warehouse_id, to_warehouse_id, product_id, transfer_date)
);
-- 声明供应链属性图
CREATE PROPERTY GRAPH supply_chain
VERTEX TABLES (
suppliers LABEL Supplier PROPERTIES (id, name, country, rating),
products LABEL Product PROPERTIES (id, name, category, unit_price),
warehouses LABEL Warehouse PROPERTIES (id, name, location, capacity)
)
EDGE TABLES (
supplies
SOURCE KEY (supplier_id) REFERENCES suppliers (id)
DESTINATION KEY (product_id) REFERENCES products (id)
LABEL Supplies PROPERTIES (unit_cost, lead_days),
stocked_in
SOURCE KEY (product_id) REFERENCES products (id)
DESTINATION KEY (warehouse_id) REFERENCES warehouses (id)
LABEL StockedIn PROPERTIES (quantity, reorder_level),
transfers
SOURCE KEY (from_warehouse_id) REFERENCES warehouses (id)
DESTINATION KEY (to_warehouse_id) REFERENCES warehouses (id)
LABEL Transfers PROPERTIES (quantity, transfer_date)
);
-- 查询 1:找某产品的所有供应商
SELECT supplier_name, unit_cost, lead_days
FROM GRAPH_TABLE (
supply_chain
MATCH (s:Supplier)-[sup:Supplies]->(p:Product WHERE p.name = '高精度传感器')
COLUMNS (s.name AS supplier_name, sup.unit_cost, sup.lead_days)
)
ORDER BY unit_cost;
-- 查询 2:找某供应商提供的所有产品,以及这些产品在哪些仓库有库存
SELECT product_name, warehouse_name, quantity
FROM GRAPH_TABLE (
supply_chain
MATCH (s:Supplier WHERE s.name = '深圳精密电子')
-[sup:Supplies]->(p:Product)
-[st:StockedIn]->(w:Warehouse)
COLUMNS (p.name AS product_name, w.name AS warehouse_name, st.quantity)
)
ORDER BY product_name, quantity DESC;
-- 查询 3:找从供应商到仓库的完整路径
SELECT supplier_name, product_name, warehouse_name, lead_days, stock_qty
FROM GRAPH_TABLE (
supply_chain
MATCH (s:Supplier WHERE s.country = '中国')
-[sup:Supplies]->(p:Product WHERE p.category = '电子元件')
-[st:StockedIn]->(w:Warehouse WHERE w.location LIKE '华东%')
COLUMNS (
s.name AS supplier_name,
p.name AS product_name,
w.name AS warehouse_name,
sup.lead_days,
st.quantity AS stock_qty
)
)
WHERE stock_qty > 0
ORDER BY lead_days;
-- 查询 4:找有过调拨关系的仓库对
SELECT from_wh, to_wh, product_name, total_qty
FROM GRAPH_TABLE (
supply_chain
MATCH (w1:Warehouse)-[t:Transfers]->(w2:Warehouse)
COLUMNS (
w1.name AS from_wh,
w2.name AS to_wh,
t.product_id,
t.quantity AS total_qty
)
) gt
JOIN products p ON p.id = gt.product_id
ORDER BY total_qty DESC;
7.3 性能测试:SQL/PGQ vs 等价 JOIN
-- SQL/PGQ 方式
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT friend_name
FROM GRAPH_TABLE (
social_graph
MATCH (a:Person WHERE a.name = 'Jan')-[k:Knows]->(b:Person)
COLUMNS (b.name AS friend_name)
);
-- 等价 JOIN 方式
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT b.name AS friend_name
FROM people a
JOIN friendships k ON k.person_a = a.id
JOIN people b ON b.id = k.person_b
WHERE a.name = 'Jan';
-- 你会发现执行计划几乎完全一致
-- 这就是重写器的力量——语法糖,但不是黑盒
八、总结展望:PostgreSQL 的进化之路
8.1 PG19 的战略意义
PostgreSQL 19 的发布标志着几个重要趋势:
图查询成为标准特性:SQL/PGQ 的实现意味着图查询不再是专用数据库的专利。对于大多数"轻度"图查询需求,你现在可以留在 PostgreSQL 生态中完成,而不需要引入 Neo4j、JanusGraph 等额外的技术栈。
性能优化的系统化:从 SIMD 到 Radix Sort,从并行 Vacuum 到异步 I/O 优化,PG19 的性能改进覆盖了数据库的核心路径。这不是某个单一特性的突破,而是整体工程质量的提升。
运维友好度持续提升:在线校验和、锁定统计视图、vacuum 评分系统——这些看似"无趣"的特性,对于 7×24 运行的生产系统来说,恰恰是最有价值的。
8.2 SQL/PGQ 的未来
PG19 是 SQL/PGQ 的首个实现,未来可以期待:
- 可变长度路径支持:
-[k:Knows*1..5]->语法 - 路径算法:最短路径、全最短路径
- 图算法扩展:PageRank、社区发现、中心度计算
- 更好的图专用优化:为图遍历模式定制化的执行策略
8.3 对开发者的建议
- Beta 阶段不要用于生产:PG19 目前还是 Beta 1,API 和行为可能变化。在测试环境充分验证后再考虑升级。
- 评估 SQL/PGQ 的适用性:如果你的业务有图查询需求(社交网络、供应链、权限模型),评估是否可以在 PG19 中统一解决。
- 关注性能基线:LZ4 默认压缩、并行 vacuum、JIT 默认关闭——这些改动都会影响你的性能特征,升级后建议重新进行基准测试。
- 提前检查兼容性:特别是 inet/cidr 索引操作类变更和 RADIUS 认证移除。
PostgreSQL 正在从一个"可靠的关系型数据库"进化为一个"全功能数据平台"。SQL/PGQ 的加入不是一个噱头,而是让 PostgreSQL 真正具备了处理多模态数据查询的能力——关系型、文档型、图型,全部在一个引擎中完成。
这是 PostgreSQL 35 年历史中的又一次重要进化。