PostgreSQL 19 深度解析:SQL/PGQ图查询、REPACK表重整与并行Autovacuum——三十而立的数据库之王如何重新定义关系型数据库的边界
一、引言:三十而立的PostgreSQL,又一次证明自己
2026年6月4日,PostgreSQL全球开发组正式发布了PostgreSQL 19 Beta 1。这个时间节点意义非凡——自1996年首次发布以来,PostgreSQL已经走过了整整三十年。三十年间,它从一个学术项目成长为全球最流行的开源关系型数据库,在DB-Engines排名中常年位居第四,在所有关系型数据库中仅次于Oracle、MySQL和SQL Server。
但PostgreSQL从不满足于"关系型数据库"的定位。每一次大版本发布,它都在蚕食其他数据库品类的领地:PostgreSQL 9.4引入JSONB,抢了文档数据库的生意;PostgreSQL 10引入逻辑复制,抢了数据同步工具的生意;PostgreSQL 14引入multirange类型,抢了时序数据库的生意;PostgreSQL 18引入原生向量搜索(pgvector整合),抢了向量数据库的生意。
而PostgreSQL 19,它来抢图数据库的生意了。
SQL/PGQ(Property Graph Queries)的正式引入,意味着你可以在关系型表上直接执行图查询,无需Neo4j、无需JanusGraph、无需任何额外的图数据库。与此同时,REPACK命令终结了VACUUM FULL和CLUSTER长达二十年的混乱,并行Autovacuum让大表清理不再是DBA的噩梦,在线Checksum开关让运维不再需要停机,LZ4压缩默认化让存储效率再上一个台阶。
这篇文章将从架构原理到代码实战,逐一拆解PostgreSQL 19的每一个重要特性。无论你是DBA、后端开发者还是数据库内核爱好者,这都是一份值得收藏的技术指南。
二、SQL/PGQ:在关系型表上跑图查询——告别图数据库的时代来了
2.1 什么是SQL/PGQ
SQL/PGQ是ISO/IEC 9075-16:2023标准定义的图查询语法,它允许在SQL中声明属性图(Property Graph),并使用类似Cypher的模式匹配语法进行图遍历查询。PostgreSQL 19实现的是SQL/PGQ——作为SQL语言的内嵌扩展,而不是独立的图查询语言。
简单来说,你可以:
- 把现有的关系表"声明"为一个属性图(顶点和边)
- 使用GRAPH_TABLE语法进行模式匹配查询
- 查询结果仍然是标准的关系型结果集,可以和普通SQL无缝组合
2.2 为什么不需要图数据库了
图数据库的核心价值在于一件事:路径查询。
当你需要"找出A和B之间所有长度不超过5的路径"或者"找出和A有3层以内关系的所有人"时,在纯SQL中你需要写递归CTE(Common Table Expression),代码复杂度和可读性都很差。而在图查询语言中,这可能只需要一行代码。
但图数据库的问题也很明显:
- 数据同步:你需要把关系型数据库中的数据ETL到图数据库,增加了一条数据管道
- 事务一致性:图数据库和关系型数据库之间的事务一致性很难保证
- 运维成本:多一套数据库意味着多一套备份、监控、扩容方案
- 查询能力:大多数图数据库的聚合、排序、分页能力远不如SQL
SQL/PGQ的出现,让你在PostgreSQL内部就能获得图查询能力,同时保留SQL的全部优势。
2.3 实战:在PostgreSQL 19中使用SQL/PGQ
假设我们有一个社交网络场景,有用户表和好友关系表:
-- 创建基础表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT,
city TEXT
);
CREATE TABLE friendships (
user_id INT REFERENCES users(id),
friend_id INT REFERENCES users(id),
since DATE,
PRIMARY KEY (user_id, friend_id)
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
content TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE likes (
user_id INT REFERENCES users(id),
post_id INT REFERENCES posts(id),
PRIMARY KEY (user_id, post_id)
);
-- 插入测试数据
INSERT INTO users (name, age, city) VALUES
('Alice', 30, 'Beijing'),
('Bob', 25, 'Shanghai'),
('Charlie', 35, 'Beijing'),
('Diana', 28, 'Shenzhen'),
('Eve', 22, 'Beijing');
INSERT INTO friendships VALUES
(1, 2, '2024-01-15'),
(2, 3, '2024-03-20'),
(1, 4, '2024-06-10'),
(3, 5, '2024-09-01'),
(4, 5, '2025-01-05');
INSERT INTO posts (user_id, content) VALUES
(1, 'Hello World'), (2, 'PG19 is great'), (3, 'Graph queries!');
INSERT INTO likes VALUES (1, 2), (2, 3), (3, 1);
声明属性图
-- 将关系表映射为属性图
CREATE PROPERTY GRAPH social_network
VERTEX TABLES (
users
VERTEX KEY (id)
PROPERTIES (name, age, city),
posts
VERTEX KEY (id)
PROPERTIES (content, created_at)
)
EDGE TABLES (
friendships
SOURCE KEY (user_id) REFERENCES users (id)
DESTINATION KEY (friend_id) REFERENCES users (id)
PROPERTIES (since),
likes
SOURCE KEY (user_id) REFERENCES users (id)
DESTINATION KEY (post_id) REFERENCES posts (id)
);
执行图查询
-- 查询1:找出Alice的所有朋友
SELECT person.name
FROM GRAPH_TABLE (
social_network
MATCH (person:users) -[f:friendships]-> (friend:users)
WHERE person.name = 'Alice'
COLUMNS (friend.name AS person_name)
);
-- 查询2:找出Alice的朋友的朋友(二度关系)
SELECT DISTINCT p2.name AS friend_of_friend
FROM GRAPH_TABLE (
social_network
MATCH (p1:users) -[f1:friendships]-> (p2:users) -[f2:friendships]-> (p3:users)
WHERE p1.name = 'Alice' AND p3.name <> 'Alice'
COLUMNS (p3.name AS friend_of_friend)
);
-- 查询3:找出所有互相认识的人(双向友谊)
SELECT p1.name, p2.name
FROM GRAPH_TABLE (
social_network
MATCH (p1:users) -[f1:friendships]-> (p2:users) -[f2:friendships]-> (p1:users)
WHERE p1.id < p2.id
COLUMNS (p1.name AS person1, p2.name AS person2)
);
-- 查询4:找出谁喜欢Alice发的帖子
SELECT liker.name
FROM GRAPH_TABLE (
social_network
MATCH (liker:users) -[l:likes]-> (post:posts) <-[w:posts]- (author:users)
WHERE author.name = 'Alice'
COLUMNS (liker.name AS liker_name)
);
2.4 架构原理:PGQ不是图存储引擎
PostgreSQL 19的SQL/PGQ实现有一个关键设计决策:它不是图存储引擎,而是一个图查询重写器。
当你创建PROPERTY GRAPH时,PostgreSQL只是存储了图的元数据(哪些表是顶点、哪些表是边、属性如何映射)。当你执行GRAPH_TABLE查询时,查询规划器会把它重写为标准的关系型查询——本质上是JOIN操作。
这意味着:
- 零数据冗余:不需要额外存储图数据,图就是你的关系表
- 事务一致性:图查询和普通SQL在同一个事务中,ACID保证
- 优化器加持:图查询会受益于PostgreSQL查询优化器的所有改进
- 扩展性:你可以随时添加新的顶点和边类型
2.5 与Cypher/Neo4j的对比
| 维度 | SQL/PGQ (PostgreSQL 19) | Cypher (Neo4j) |
|---|---|---|
| 数据存储 | 关系表,零冗余 | 原生图存储 |
| 查询语言 | SQL内嵌,GRAPH_TABLE子句 | 独立的Cypher语言 |
| 事务支持 | 完整ACID | 最终一致性(CE版本) |
| 聚合/排序 | 完整SQL能力 | 有限 |
| 生态工具 | 全部SQL生态 | Neo4j专属生态 |
| 性能(路径查询) | 依赖JOIN优化 | 原生图遍历优化 |
| 性能(聚合分析) | 强 | 弱 |
结论:如果你的查询以路径遍历为主(如社交网络、推荐引擎),且数据量极大(数十亿边),专用图数据库仍有优势。但如果你的查询80%是关系型、20%是图遍历,PostgreSQL 19的SQL/PGQ是更优雅的方案——你不需要维护两套数据库。
三、REPACK:终结VACUUM FULL和CLUSTER二十年的混乱
3.1 历史遗留问题
PostgreSQL的MVCC(多版本并发控制)机制有一个众所周知的副作用:表膨胀(Table Bloat)。每次UPDATE操作都会产生新版本的行,旧版本的行需要VACUUM来清理。但普通的VACUUM只能标记空间为可重用,不能真正归还给操作系统。
为了真正压缩表空间,PostgreSQL历史上提供了两个命令:
VACUUM FULL:重写整个表,获取排他锁CLUSTER:按索引顺序重写表,获取排他锁
这两个命令的问题是:
- 名字令人困惑:VACUUM FULL听起来像"更强力的VACUUM",但其实完全不同
- 都需要排他锁:执行期间表完全不可用
- 功能重叠:两者做的事情几乎一样,只是排序策略不同
- 第三方方案:pg_repack扩展应运而生,但需要额外安装
3.2 REPACK:统一的解决方案
PostgreSQL 19引入了全新的REPACK命令,一举取代VACUUM FULL和CLUSTER:
-- 基本用法:重写表,回收空间
REPACK my_table;
-- 按索引顺序重写(取代CLUSTER)
REPACK my_table USING my_index;
-- 并发模式:不阻塞读写(取代pg_repack)
REPACK my_table CONCURRENTLY;
3.3 REPACK CONCURRENTLY的实现原理
REPACK CONCURRENTLY是PostgreSQL 19最令人兴奋的特性之一。它允许在不获取排他锁的情况下重整表空间,整个过程表保持可读写状态。
其实现原理如下:
- 创建复制槽:首先创建一个逻辑复制槽,用于捕获REPACK期间的所有变更
- 创建新表文件:在后台创建一个新的表文件,按指定顺序写入数据
- 应用增量变更:在新表构建期间,通过复制槽捕获原表的INSERT/UPDATE/DELETE操作,并在新表上重放
- 原子切换:当新表和原表的差异足够小时,短暂获取排他锁,完成最后的增量同步,然后原子切换表文件
-- 实战:对一个膨胀的大表进行并发重整
-- 假设 orders 表因为频繁更新导致严重膨胀
-- 查看表膨胀情况
SELECT
schemaname || '.' || tablename AS table_name,
pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS total_size,
pg_size_pretty(pg_relation_size(schemaname || '.' || tablename)) AS table_size,
n_dead_tup,
n_live_tup,
ROUND(n_dead_tup::numeric / GREATEST(n_live_tup, 1) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY n_dead_tup DESC;
-- 并发重整(不影响业务)
REPACK orders CONCURRENTLY;
-- 查看重整后的大小
SELECT pg_size_pretty(pg_relation_size('orders'));
3.4 相关配置参数
-- 设置REPACK使用的最大复制槽数(默认3)
SET max_repack_replication_slots = 5;
-- 查看当前复制槽使用情况
SELECT slot_name, active, restart_lsn
FROM pg_replication_slots;
3.5 与pg_repack扩展的对比
| 维度 | PostgreSQL 19 REPACK | pg_repack扩展 |
|---|---|---|
| 安装 | 内置,无需额外安装 | 需要单独安装 |
| 并发模式 | CONCURRENTLY选项 | 默认就是并发的 |
| 索引重建 | 支持 | 支持 |
| 批量操作 | 单表 | 支持全库 |
| 依赖 | 需要复制槽 | 需要触发器 |
| 成熟度 | 首个版本,Beta | 生产就绪多年 |
建议:PostgreSQL 19正式发布后,REPACK将逐步取代pg_repack。但在初期,对于关键业务系统,建议继续使用pg_repack直到REPACK经过更多生产验证。
四、并行Autovacuum:大表清理不再是DBA的噩梦
4.1 老问题:单线程Vacuum太慢
在PostgreSQL 18及之前版本中,Autovacuum是单线程执行的。对于一张有数十亿行、几十个索引的大表,一次VACUUM可能需要数小时。在这期间:
- 表空间持续膨胀
- 查询性能下降
- 事务ID回卷风险增加
手动VACUUM从PostgreSQL 13开始支持并行(VACUUM (PARALLEL 4)),但Autovacuum一直不支持。
4.2 PostgreSQL 19的并行Autovacuum
PostgreSQL 19终于为Autovacuum引入了并行能力。新的配置参数:
-- 集群级:最大并行Autovacuum Worker数(默认0,即关闭)
SET autovacuum_max_parallel_workers = 4;
-- 表级:针对特定表设置并行Worker数
ALTER TABLE huge_table SET (
autovacuum_parallel_workers = 3
);
4.3 并行化的范围:不是你想的那样
关键点:并行Autovacuum只并行化索引清理阶段(Index Cleanup),不并行化堆扫描(Heap Scan)和堆截断(Heap Truncation)。
工作流程:
- Leader进程:扫描Heap,构建Dead Tuple列表(单线程)
- Worker进程:多个Worker同时清理不同的索引(并行)
- Leader进程:清理Heap,截断尾部空页(单线程)
-- 一张有5个索引的表,使用并行Autovacuum
-- Leader + 4个Worker,每个Worker负责一个索引
-- Leader同时清理第5个索引
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
event_type TEXT,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now(),
user_id BIGINT,
session_id TEXT
);
-- 创建多个索引
CREATE INDEX idx_events_type ON events (event_type);
CREATE INDEX idx_events_user ON events (user_id);
CREATE INDEX idx_events_session ON events (session_id);
CREATE INDEX idx_events_created ON events (created_at);
CREATE INDEX idx_events_payload ON events USING GIN (payload);
-- 设置该表使用3个并行Worker
ALTER TABLE events SET (
autovacuum_parallel_workers = 3
);
4.4 什么时候并行Autovacuum真正有用
有用的场景:
- 表有多个索引(特别是GIN、GiST等代价高的索引)
- 索引清理是VACUUM的主要耗时来源
- 宽表场景,单行数据量大
没用的场景:
- 只有一个主键B-tree索引的表:Leader自己就能完成,没有Worker可分配
- 瓶颈在Heap Scan而非Index Cleanup的场景
- 索引很小,清理本身就很快
-- 诊断:查看VACUUM各阶段耗时
-- PostgreSQL 19新增了更详细的VACUUM进度报告
SELECT
relid::regclass,
phase,
heap_blks_total,
heap_blks_scanned,
heap_blks_vacuumed,
index_vacuum_count,
max_dead_tuples,
num_dead_tuples
FROM pg_stat_progress_vacuum;
4.5 Autovacuum评分系统
PostgreSQL 19还引入了一个全新的Autovacuum评分系统,用于更智能地决定哪些表应该优先被清理:
-- 新的评分权重参数
SET autovacuum_vacuum_score_weight = 10; -- 死元组权重
SET autovacuum_freeze_score_weight = 5; -- 事务ID冻结权重
SET autovacuum_multixact_freeze_score_weight = 5; -- MultiXact冻结权重
SET autovacuum_analyze_score_weight = 2; -- 统计信息过时权重
SET vacuum_insert_score_weight = 1; -- 插入统计权重
评分越高的表,越优先被Autovacuum处理。这比之前的简单阈值机制更加灵活和智能。
五、在线Checksum:终于不用停机切换数据校验了
5.1 老问题:Checksum是初始化时的决定
在PostgreSQL 18及之前版本中,数据页Checksum(校验和)只能在initdb时开启或关闭。如果你在创建集群时忘了开启Checksum,想要事后开启,唯一的办法是:
- 停止数据库
- 使用
pg_checksums工具离线转换 - 重新启动数据库
对于7×24运行的生产系统,这几乎不可接受。
5.2 PostgreSQL 19的在线Checksum
PostgreSQL 19允许你在数据库运行时在线开启或关闭Checksum:
-- 在线开启Checksum
ALTER SYSTEM SET data_checksums = on;
SELECT pg_reload_conf();
-- 查看Checksum状态
SHOW data_checksums;
-- 或者使用函数查看
SELECT * FROM pg_checksums_status();
5.3 实现原理
在线Checksum的实现涉及以下步骤:
- 后台转换:系统启动后台Worker,逐页读取数据文件并计算Checksum
- 增量进行:转换是增量的,不会一次性占用大量I/O
- 新页即时生效:转换开始后,所有新写入的页面立即带有Checksum
- 旧页逐步覆盖:旧页面在被读取或写入时逐步添加Checksum
-- 监控Checksum转换进度
SELECT
datname,
checksum_version,
pages_total,
pages_checksummed,
ROUND(pages_checksummed::numeric / GREATEST(pages_total, 1) * 100, 2) AS progress_pct
FROM pg_database d
JOIN pg_checksum_progress p ON d.oid = p.dboid;
5.4 生产建议
- 所有新集群都应该开启Checksum:这是数据完整性的基本保障
- 已有集群建议在线开启:特别是使用云存储(如EBS、云盘)时,底层存储可能静默损坏数据
- 监控Checksum错误:定期检查日志中的Checksum校验失败
-- 检查Checksum错误
SELECT * FROM pg_stat_database WHERE datname = current_database();
-- 查看checksum_failures列
六、性能优化:从排序到压缩,全面提升
6.1 LZ4成为默认TOAST压缩算法
-- PostgreSQL 19之前:默认使用pglz压缩
-- PostgreSQL 19之后:默认使用lz4压缩
-- 查看当前默认压缩算法
SHOW default_toast_compression;
-- 输出: lz4
-- 对于已有的表,新插入/更新的数据会使用lz4
-- 旧数据仍然使用pglz,直到被重写
-- 手动指定压缩算法
ALTER TABLE my_table ALTER COLUMN big_text SET COMPRESSION lz4;
-- 或者
ALTER TABLE my_table ALTER COLUMN big_text SET COMPRESSION pglz;
-- 性能对比
-- lz4: 压缩率约2.5-3x,压缩/解压速度极快(GB/s级别)
-- pglz: 压缩率约2-3x,压缩/解压速度较慢(百MB/s级别)
6.2 基数排序(Radix Sort)加速
PostgreSQL 19将排序算法从传统的QuickSort升级为Radix Sort,对于大结果集的排序操作有显著提升:
-- 大量数据排序的场景
-- 例如:分析查询中的ORDER BY
EXPLAIN ANALYZE
SELECT user_id, COUNT(*) AS cnt
FROM events
GROUP BY user_id
ORDER BY cnt DESC
LIMIT 100;
-- PostgreSQL 19中,如果排序键是定长类型(如int, bigint, timestamp)
-- 会自动使用基数排序,性能提升可达30-50%
6.3 SIMD加速COPY FROM
PostgreSQL 19使用SIMD(单指令多数据)指令优化了文本和CSV格式的COPY FROM操作:
-- 大批量数据导入
COPY events (event_type, payload, created_at, user_id, session_id)
FROM '/data/events.csv'
WITH (FORMAT csv, HEADER true);
-- PostgreSQL 19使用SIMD指令并行解析字段分隔符和换行符
-- 对于宽表(每行数据量大),性能提升可达2-3倍
6.4 异步I/O改进
-- PostgreSQL 19改进了异步I/O的预读调度
-- 新的配置参数
SET io_method = 'worker'; -- 使用Worker模式的异步I/O
SET io_min_workers = 3;
SET io_max_workers = 16;
SET io_worker_idle_timeout = '5s';
SET io_worker_launch_interval = '100ms';
-- 查看I/O统计
SELECT * FROM pg_stat_io;
6.5 外键约束检查优化
PostgreSQL 19优化了外键约束检查的性能,特别是在大批量INSERT操作中:
-- 批量插入场景
-- PostgreSQL 19会合并对外键引用表的多次查找
-- 减少I/O操作次数
INSERT INTO orders (customer_id, product_id, quantity)
SELECT
(random() * 1000)::int,
(random() * 100)::int,
(random() * 10 + 1)::int
FROM generate_series(1, 100000);
-- 在PostgreSQL 19中,外键检查的性能提升可达5-10倍
七、逻辑复制增强:序列同步与增量WAL解码
7.1 序列值同步
PostgreSQL 19终于解决了逻辑复制中序列不同步的老问题:
-- 创建发布(包含序列)
CREATE PUBLICATION my_pub FOR ALL TABLES, ALL SEQUENCES;
-- 创建订阅时自动同步序列
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=primary dbname=mydb'
PUBLICATION my_pub
WITH (copy_data = true);
-- 手动刷新序列值
ALTER SUBSCRIPTION my_sub REFRESH SEQUENCES;
-- 查看序列同步状态
SELECT * FROM pg_stat_subscription_stats;
7.2 不重启启用WAL逻辑解码
-- PostgreSQL 19之前:wal_level从replica改为logical需要重启
-- PostgreSQL 19之后:可以在线切换
-- 即使wal_level设置为replica
-- 当创建逻辑复制订阅时,系统会自动提升WAL级别
SHOW wal_level; -- replica
SHOW effective_wal_level; -- logical(自动提升)
7.3 PUBLICATION的EXCEPT子句
-- 发布所有表,但排除特定表
CREATE PUBLICATION my_pub FOR ALL TABLES
EXCEPT TABLE temp_data, TABLE audit_log;
-- 这在大型数据库中非常有用
-- 你不需要逐一列出所有要发布的表
八、查询优化器:更多聪明的重写
8.1 NOT IN转Anti-Join
-- PostgreSQL 19可以将NOT IN子查询转换为更高效的Anti-Join
-- 当子查询结果不包含NULL时
-- 之前:可能使用SubPlan
-- 之后:自动使用Anti-Join
SELECT * FROM orders
WHERE customer_id NOT IN (
SELECT id FROM customers WHERE status = 'banned'
);
-- 用EXPLAIN查看执行计划
EXPLAIN SELECT * FROM orders
WHERE customer_id NOT IN (
SELECT id FROM customers WHERE status = 'banned'
);
-- PostgreSQL 19: Anti Join
-- PostgreSQL 18: SubPlan
8.2 聚合下推到Join之前
-- 当Join会放大行数时,先聚合再Join可以减少处理的行数
-- 例如:统计每个客户的订单总金额
SELECT c.name, SUM(o.amount)
FROM customers c
JOIN orders o ON o.customer_id = c.id
GROUP BY c.name;
-- PostgreSQL 19可能将聚合下推到orders表上先执行
-- 减少Join处理的行数
8.3 Window函数支持IGNORE NULLS
-- 新的IGNORE NULLS选项
-- 对lead, lag, first_value, last_value, nth_value有效
-- 例如:取前一个非空值
SELECT
ts,
value,
lag(value IGNORE NULLS) OVER (ORDER BY ts) AS prev_non_null
FROM sensor_data;
8.4 INSERT ON CONFLICT DO SELECT
-- 新语法:插入冲突时返回冲突行
INSERT INTO users (email, name)
VALUES ('alice@example.com', 'Alice')
ON CONFLICT (email) DO
SELECT id, email, name FROM users WHERE email = 'alice@example.com'
RETURNING *;
-- 还可以加锁
INSERT INTO inventory (product_id, quantity)
VALUES (1, 10)
ON CONFLICT (product_id) DO
SELECT * FROM inventory WHERE product_id = 1
FOR UPDATE
RETURNING *;
九、安全增强:MD5退役与密码过期警告
9.1 MD5密码认证的最终警告
-- PostgreSQL 18将MD5密码标记为弃用
-- PostgreSQL 19在MD5认证成功后会发出警告
-- 查看警告
-- 日志中会出现:
-- WARNING: MD5 password authentication is deprecated
-- 禁用警告(不推荐)
SET md5_password_warnings = off;
-- 迁移到SCRAM-SHA-256
-- 步骤1:修改密码加密方式
SET password_encryption = 'scram-sha-256';
-- 步骤2:用户重新设置密码
ALTER USER myuser PASSWORD 'new_password';
-- 步骤3:修改pg_hba.conf
-- 将md5改为scram-sha-256
9.2 密码过期警告
-- 新参数:密码过期提前警告天数
SET password_expiration_warning_threshold = 7;
-- 当用户密码即将过期时,连接时会收到警告
-- 这需要配合密码有效期使用(通过角色属性设置)
9.3 RADIUS认证移除
PostgreSQL 19移除了RADIUS认证支持,因为RADIUS over UDP存在无法修复的安全漏洞。如果你的pg_hba.conf中使用了radius认证方式,需要在升级前替换为其他认证方式。
十、监控与可观测性:更多系统视图
10.1 新增系统视图
-- pg_stat_lock:锁类型统计
SELECT * FROM pg_stat_lock;
-- pg_stat_recovery:恢复状态
SELECT * FROM pg_stat_recovery;
-- pg_stat_autovacuum_scores:Autovacuum评分详情
SELECT
relid::regclass,
score,
last_autovacuum,
last_autoanalyze
FROM pg_stat_autovacuum_scores
ORDER BY score DESC
LIMIT 20;
-- pg_dsm_registry_allocations:动态共享内存详情
SELECT * FROM pg_dsm_registry_allocations;
10.2 更详细的VACUUM进度
-- PostgreSQL 19增加了VACUUM的启动者和模式信息
SELECT
relid::regclass,
phase,
started_by, -- 新列:manual, autovacuum, wraparound
mode, -- 新列:normal, aggressive
heap_blks_total,
heap_blks_scanned,
index_vacuum_count
FROM pg_stat_progress_vacuum;
10.3 进程级别的日志级别
-- 新语法:按进程类型设置日志级别
SET log_min_messages = 'autovacuum:debug1,warning';
-- 只对autovacuum进程输出debug1级别日志
-- 其他进程只输出warning及以上
十一、分区管理:合并与拆分
11.1 分区合并
-- 将多个分区合并为一个
ALTER TABLE orders
MERGE PARTITIONS (orders_2024_q1, orders_2024_q2, orders_2024_q3, orders_2024_q4)
INTO orders_2024;
11.2 分区拆分
-- 将一个分区拆分为多个
ALTER TABLE orders
SPLIT PARTITION orders_2024
AT ('2024-07-01')
INTO (PARTITION orders_2024_h1, PARTITION orders_2024_h2);
这两个操作在PostgreSQL 19之前需要手动创建新分区、迁移数据、修改路由规则,现在一条SQL搞定。
十二、升级指南与兼容性注意事项
12.1 主要不兼容变更
| 变更 | 影响 | 应对措施 |
|---|---|---|
| JIT默认关闭 | 分析型查询可能变慢 | 手动SET jit = on |
| max_locks_per_transaction默认128 | 内存占用增加 | 评估是否需要调整 |
| standard_conforming_strings强制开启 | 使用转义字符串的应用需修改 | 检查SQL中的转义字符 |
| RADIUS认证移除 | 使用RADIUS的集群无法升级 | 替换为其他认证方式 |
| MULE_INTERNAL编码移除 | 使用该编码的数据库需dump/restore | 提前迁移 |
| btree_gist inet/cidr opclass变更 | 使用该opclass的索引需重建 | 升级前检查并重建索引 |
12.2 升级步骤
# 1. 备份
pg_dumpall > backup.sql
# 2. 检查不兼容项
# 检查JIT设置
SHOW jit;
# 检查编码
SELECT datname, encoding FROM pg_database;
# 检查btree_gist索引
SELECT indexname, indexdef
FROM pg_indexes
WHERE indexdef LIKE '%btree_gist%'
AND indexdef LIKE '%inet%';
# 3. 使用pg_upgrade升级
pg_upgrade -d /old/data -D /new/data -b /old/bin -B /new/bin
# 4. 更新统计信息
vacuumdb --all --analyze-in-stages
十三、总结与展望
PostgreSQL 19是一个里程碑式的版本。三十年来,PostgreSQL从一个学术项目成长为数据库领域的"瑞士军刀",而PG19再次扩展了它的能力边界:
- SQL/PGQ让你不再需要图数据库,关系型表上直接跑图查询
- REPACK终结了VACUUM FULL和CLUSTER的混乱,并发模式让表重整不再停业务
- 并行Autovacuum让大表清理不再是DBA的噩梦
- 在线Checksum让数据完整性保障不再需要停机
- LZ4默认压缩让存储效率再上一个台阶
- 逻辑复制增强让序列同步、增量WAL解码成为现实
- 查询优化器的持续改进让复杂查询越来越快
- 安全增强推动MD5退役,引入密码过期警告
PostgreSQL的成功不是偶然。它的开放治理模式、严格的代码审查、保守的向后兼容策略,以及对SQL标准的深度拥抱,共同造就了这个三十年不衰的开源传奇。
对于开发者来说,PostgreSQL 19的建议是:
- 立即开始测试Beta版本,为正式发布做好准备
- 评估SQL/PGQ,看是否能简化你的图查询需求
- 启用LZ4压缩,免费获得存储和性能提升
- 规划并行Autovacuum,为大表清理做好配置
- 推动MD5退役,迁移到SCRAM-SHA-256
PostgreSQL的下一个三十年,从PG19开始。