编程 PostgreSQL 19 Beta 全面解读:图查询、并行清理、64 位防回卷与 DBA 必知的 10 大变化

2026-06-23 08:55:28 +0800 CST views 10

PostgreSQL 19 Beta 全面解读:图查询、并行清理、64 位防回卷与 DBA 必知的 10 大变化

2026 年 6 月 4 日,PostgreSQL Global Development Group 正式发布了 PostgreSQL 19 Beta 1。这是 PG 诞生三十周年之际最重要的一个版本——不仅引入了 SQL 标准的属性图查询(SQL/PGQ),而且在运维领域做出了多项让 DBA 夜班减少 80% 的实质性改进。

很多文章会告诉你 PG19 有了图查询,但我想换个角度——从真正影响你生产环境运维体验的变化入手,再深入那些让人眼前一亮的语法糖和架构升级。


目录

  1. 背景:三十而立的 PostgreSQL
  2. 运维篇:那些让 DBA 少熬夜的改变
  3. 架构升级:异步 I/O 与并行自动清理
  4. 开发者体验:SQL/PGQ 图查询与 GROUP BY ALL
  5. 时态数据处理:FOR PORTION OF
  6. 查询计划控制:pg_plan_advice 扩展深度解析
  7. 逻辑复制重大改进
  8. 安全与监控增强
  9. 默认配置的静默革命
  10. 升级与兼容性指南
  11. 总结与展望

背景:三十而立的 PostgreSQL

1996 年,PostgreSQL 6.0 作为 Postgres95 的继任者正式对外发布。到 2026 年,刚好三十年。

这三十年里,PG 从"学术界的关系数据库实验品"成长为 DB-Engines 排名中全球增长最快的关系型数据库——连续第三年获得"年度数据库"(DBMS of the Year)称号。支撑它的不仅仅是 ACID 事务和扩展性,更是 PostgreSQL 社区三十年如一日的工程纪律。

PG19 的功能冻结在 2026 年 4 月 8 日完成,经过近两个月的 Beta 测试周期,预计在 2026 年 9-10 月之间发布正式版。

运维篇:那些让 DBA 少熬夜的改变

64 位 MultiXact:告别"要么 VACUUM,要么死"

如果你运维过大规模的 PG 集群,你一定听说过或者亲身经历过一种噩梦:

某天凌晨 3 点,监控突然告警:数据库拒绝新事务,日志里全是 MultiXact member limit exceeded 的错误。唯一恢复手段:在应用离线的情况下执行紧急 VACUUM。

这个问题的根源在于 32 位的 MultiXact 成员计数器。在高并发工作负载下——大量 SELECT ... FOR SHARE、外键检查、行锁冲突——累积的共享行锁信息可能耗尽 40 亿的成员空间。

PG19 将其提升到 64 位。 理论上回卷问题仍然存在(2^64 确实足够大),但实践中,这种故障模式彻底消失了。

-- PG19 之前,你可能需要这样监控 MultiXact
SELECT mxid_age(mxid) AS mxid_age
FROM pg_control_checkpoint();

-- 当 mxid_age 接近 20 亿时需要警惕
-- PG19 之后,这个监控项可以删掉了

这不是一个你可以"下个版本再升级"的特性——如果你在运行高并发的 OLTP 系统,单是这个改变就值得你安排 PG19 的升级计划。

JIT 默认关闭:被误解的"性能加速器"

从 PG12 开始,JIT(Just-in-Time compilation)默认启用。但 PG19 将其默认改为关闭:

# postgresql.conf
# PG19 默认值
jit = off

为什么要改?因为 JIT 对大多数 OLTP 工作负载来说是净亏损

当 JIT 开启时,每条达到 jit_above_cost 阈值的查询都会触发 LLVM 编译。编译本身有开销——通常 10-50ms。对于 OLTP 场景下几十毫秒内完成的查询,JIT 的开销可能超过查询本身执行时间。

用数据说话:在典型 OLTP 基准测试(pgbench)中,JIT 开启比关闭慢约 5-15%,因为 CPU 时间被浪费在编译那些只执行一次的快速查询上。

那谁应该重新开启 JIT?

# OLAP/分析型工作负载 - 建议显式开启
jit = on
jit_above_cost = 100000  -- 仅对大查询进行 JIT
jit_optimize_above_cost = 500000
jit_inline_above_cost = 500000

如果你运行的是数据仓库或分析型查询,升级 PG19 之前就要在配置文件中显式设置 jit = on,否则一个原来 6 分钟的报表查询可能变成 19 分钟。

default_toast_compression = lz4

另一个值得注意的默认变更:TOAST 压缩算法从 pglz 切换为 LZ4。

-- PG19 中,未显式指定压缩算法的列默认使用 LZ4
CREATE TABLE logs (
    id bigint,
    payload text  -- 自动使用 lz4 压缩
);

-- 显式指定保持 pglz
CREATE TABLE logs_v2 (
    id bigint,
    payload text COMPRESSION pglz
);

LZ4 的压缩速度是 pglz 的 3-5 倍,解压速度更是快一个数量级。虽然压缩比略低(通常低 5-10%),但在大多数场景下,速度优势远大于压缩比的损失。特别是对于 JSONB、大文本字段等频繁读写的场景,LZ4 能让查询延迟明显降低。

架构升级:异步 I/O 与并行自动清理

异步 I/O Worker 自动伸缩

PG18 引入了异步 I/O 子系统,但需要手动配置 worker 数量。PG19 将此升级为自动伸缩:

# PG19 新增配置
io_method = worker              # 启用异步 I/O worker
io_min_workers = 2              # 最小 worker 数
io_max_workers = 8              # 最大 worker 数(根据负载自动调节)

这意味着 PG 可以更好地利用现代 NVMe SSD 的并行 IO 能力。当遇到大量顺序扫描或排序溢出的场景时,AIO 子系统会自动增加 worker 来提升吞吐量。

-- 使用 EXPLAIN ANALYZE 查看异步 I/O 统计
EXPLAIN (ANALYZE, BUFFERS, IO) SELECT * FROM big_table WHERE condition;

-- 输出中会出现 IO 相关的统计信息
-- I/O Timings: read=123.456 write=45.678
-- AIO: requests=456 bytes_read=98765432

并行自动 VACUUM

这是 PG19 中另一个对运维体验影响深远的改变。

在 PG18 及之前,autovacuum 对单张表的索引清理是串行的。如果你的表有 8 个索引,autovacuum 会逐个清理,每个索引都消耗 maintenance_work_mem 但只能利用一个 CPU 核心。

PG19 引入了 autovacuum_max_parallel_workers

# 允许 autovacuum 并行清理表上的索引
autovacuum_max_parallel_workers = 4

# 注意内存消耗:每个 worker 消耗 maintenance_work_mem
# 最坏情况:autovacuum_max_workers × autovacuum_max_parallel_workers × maintenance_work_mem
# 例如:3 × 4 × 1GB = 12GB
-- 按表设置并行度
ALTER TABLE large_table SET (autovacuum_parallel_workers = 2);

内存警告:如果你把 maintenance_work_mem 调到了 4GB 且有十几个 autovacuum worker,升级前一定要重新计算:

最大可能内存 = autovacuum_max_workers × autovacuum_max_parallel_workers × maintenance_work_mem
               = 3 × 4 × 4GB = 48GB  ← 危险!

PG19 还引入了一个自动清理评分系统,用来更智能地决定哪些表优先被清理。系统会根据表的大小、修改率、可见性映射的状态等维度综合打分,而不是简单地按最近修改时间排队。

Vacuum 的"扫描中标记可见"策略

PG19 的 vacuum 新增了一个巧妙策略:当普通查询在扫描表时,vacuum 可以趁机把页面标记为可见。这意味着:

  • 后续的 vacuum 工作减少(更少页面需要处理)
  • 查询看到的行版本更少(加速后续查询)

这是一个"空手套白狼"的优化——查询已经付出了扫描页面的成本,vacuum 顺便把标记做了,几乎零额外开销。

REPACK 命令

表膨胀是 PG DBA 最常见的头痛之一。过去需要 pg_repack 扩展或 VACUUM FULL(表级锁)来解决。PG19 带来了原生的 REPACK 命令:

-- 基本用法
REPACK TABLE bloated_table;

-- 非阻塞模式(允许并发读写)
REPACK TABLE bloated_table CONCURRENTLY;

CONCURRENTLY 模式的工作原理类似于 CREATE INDEX CONCURRENTLY

  1. 创建一个新表来接收数据
  2. 在后台拷贝数据
  3. 在切换时短暂加锁
  4. 应用程序几乎无感知
-- 更适合生产环境的批处理
DO $$
DECLARE
    tbl text;
BEGIN
    FOR tbl IN 
        SELECT schemaname || '.' || relname
        FROM pg_stat_user_tables
        WHERE n_live_tup > 100000 
          AND n_dead_tup::float / GREATEST(n_live_tup, 1) > 0.3
    LOOP
        EXECUTE format('REPACK TABLE %s CONCURRENTLY', tbl);
    END LOOP;
END $$;

外键检查性能提升 2x

高并发插入场景中,外键约束的检查开销一直是个隐形成本。PG19 优化了外键检查的索引查找路径,在存在外键约束的 INSERT 场景中性能提升可达 2 倍

-- 测试:PG18 vs PG19 的外键插入性能
-- 设置:orders 表通过 customer_id 引用 customers 表
-- 场景:批量插入 10 万条订单记录

-- PG18:平均耗时 4.2 秒
-- PG19:平均耗时 2.1 秒

这对于电商订单系统、日志写入场景等高频插入且含外键引用的表来说,是实实在在的吞吐量提升。

开发者体验:SQL/PGQ 图查询与 GROUP BY ALL

SQL/PGQ:标准化的属性图查询

这可能是 PG19 最"上头条"的特性。SQL/PGQ(SQL Property Graph Queries)是 SQL 标准的一部分,允许你使用标准 SQL 语法执行图查询,而不需要安装额外的扩展(如 Apache AGE)或切换到专门的图数据库。

核心概念:属性图(Property Graph)由顶点(Vertex)和(Edge)组成。顶点和边都可以带有属性(键值对)。在 PG19 中,你可以通过 CREATE PROPERTY GRAPH 在已有的表上定义图结构:

-- 假设已有关系型表
CREATE TABLE persons (
    id bigint PRIMARY KEY,
    name text,
    age int,
    city text
);

CREATE TABLE knows (
    id bigint PRIMARY KEY,
    person_a bigint REFERENCES persons(id),
    person_b bigint REFERENCES persons(id),
    since date,
    relationship_type text
);

-- 定义属性图(不复制数据,仅定义映射)
CREATE PROPERTY GRAPH social_network
  VERTEX TABLES (
    persons KEY (id) LABEL person
      PROPERTIES (id, name, age, city)
  )
  EDGE TABLES (
    knows KEY (id) SOURCE KEY (person_a) REFERENCES persons (id)
                 DESTINATION KEY (person_b) REFERENCES persons (id)
      LABEL knows
      PROPERTIES (since, relationship_type)
  );

一旦定义了属性图,就可以使用图路径查询:

-- 找到"张三"认识的所有人
SELECT person.name, knows.since
FROM social_network MATCH (person) -[knows]-> (friend)
WHERE person.name = '张三';

-- 多跳查询:找到朋友的朋友(二度关系)
SELECT p1.name AS person, p2.name AS friend_of_friend
FROM social_network MATCH (p1) -[:knows]-> (p2) -[:knows]-> (p3)
WHERE p1.name = '张三' AND p3.name <> '张三';

-- 可变路径查询:一到三度关系
SELECT p1.name AS person, p3.name AS reachable, length(path) AS hops
FROM social_network MATCH (p1) -[:knows]->{1,3} (p3)
WHERE p1.name = '张三';

这对实际业务意味着什么?

  • 社交网络:关注链、推荐算法
  • 金融风控:资金流向追踪、关联交易检测
  • 供应链:物料溯源、依赖分析
  • 权限系统:角色继承路径、资源访问链

想象一下:你的电商数据库里已经有订单、用户、商品、物流表,现在你可以用图查询找到"购买过同一商品的所有用户,再找出他们购买的其他商品"——全在 PG 内部完成,不需要导出数据到 Neo4j。

用户 A -> 购买 -> 商品 X
                   |
                   v
用户 B -> 购买 -> 商品 X
                   |
                   v
用户 C -> 购买 -> 商品 Y <- 推荐给 A
-- "买了同样商品的人还买了什么" 推荐查询
WITH product_graph AS MATERIALIZED (
    SELECT p1.product_id AS source, p2.product_id AS target, COUNT(*) AS weight
    FROM orders p1
    JOIN orders p2 USING (user_id)
    WHERE p1.product_id <> p2.product_id
    GROUP BY p1.product_id, p2.product_id
)
CREATE PROPERTY GRAPH product_recommendations
  VERTEX TABLES (products)
  EDGE TABLES (product_graph);

-- 找出购买商品 42 的用户可能感兴趣的其他商品
SELECT target_products.name, edge.weight
FROM product_recommendations MATCH (p:products {id: 42}) -[e]-> (target_products)
ORDER BY edge.weight DESC
LIMIT 10;

注意:SQL/PGQ 在 PG19 中的实现是符合 SQL:2023 标准的第一阶段,后续版本还会继续增强。Beta 阶段可以先试用语法,但正式的图查询优化器可能在 GA 版本中进一步调优。

GROUP BY ALL

写 SQL 时最烦的是什么?SELECT 里写了 10 列,GROUP BY 还得重复一遍,少写一列就报错。

PG19 引入了 GROUP BY ALL

-- PG19 之前
SELECT department, role, COUNT(*), AVG(salary)
FROM employees
GROUP BY department, role;

-- PG19 之后
SELECT department, role, COUNT(*), AVG(salary)
FROM employees
GROUP BY ALL;
-- GROUP BY ALL 自动包含所有非聚合、非窗口的输出列

这在 ad-hoc 分析、报表生成等场景中能明显提升编写效率。语义非常直接:把 SELECT 列表中所有不是聚合函数、不是窗口函数、不是常量的列,自动加入 GROUP BY 子句。

Jsonpath 新增字符串函数

PG19 为 jsonpath 增加了常用的字符串处理函数,让 JSON 数据的处理能力更加完整:

-- 新的 jsonpath 字符串函数
SELECT jsonb_path_query(
    '{"name": "hello world"}',
    '$.name.upper()'
);
-- 返回 "HELLO WORLD"

-- 可用函数列表
$.name.lower()          -- 转小写
$.name.upper()          -- 转大写
$.name.initcap()        -- 首字母大写
$.name.replace("old", "new")  -- 字符串替换
$.name.split_part(",", 1)     -- 分割取第 1 部分
$.name.trim()           -- 去除两端空格
$.name.ltrim()          -- 去除左端空格
$.name.rtrim()          -- 去除右端空格

-- 实战:从 JSON 日志中提取并清洗数据
SELECT jsonb_path_query(
    event_data,
    '$.message.trim().upper()'
) AS cleaned_message
FROM event_log
WHERE event_type = 'system';

INSERT ... ON CONFLICT DO SELECT ... RETURNING

Upsert 场景下的一个实用补充:当发生冲突时,可以返回冲突行的数据。

-- 之前:ON CONFLICT DO NOTHING 不返回任何行
INSERT INTO counters (key, value) VALUES ('hits', 1)
ON CONFLICT (key) DO UPDATE SET value = counters.value + 1
RETURNING *;
-- 返回更新后的行

-- PG19 新增:当冲突时,返回冲突行数据
INSERT INTO counters (key, value) VALUES ('hits', 1)
ON CONFLICT (key) DO SELECT * RETURNING *;
-- 如果 'hits' 已存在,返回现有行数据,不修改

这个特性在幂等性去重场景中特别有用:你希望安全地插入一条记录,但如果已存在则读取现有记录。

ALTER TABLE MERGE/SPLIT PARTITIONS

分区表的运维终于变得更灵活了:

-- 创建分区表
CREATE TABLE measurements (
    id bigserial,
    measured_at date NOT NULL,
    value float8
) PARTITION BY RANGE (measured_at);

-- 创建月度分区
CREATE TABLE measurements_2026_01 PARTITION OF measurements
    FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE measurements_2026_02 PARTITION OF measurements
    FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE measurements_2026_03 PARTITION OF measurements
    FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
CREATE TABLE measurements_2026_04 PARTITION OF measurements
    FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');

-- 将 1 月和 2 月合并为一个季度分区
ALTER TABLE measurements MERGE PARTITIONS (
    measurements_2026_01, measurements_2026_02
) INTO measurements_2026_q1;

-- 将一个分区拆分为两个
ALTER TABLE measurements SPLIT PARTITION measurements_2026_04
    INTO (
        PARTITION measurements_2026_04_early
            FOR VALUES FROM ('2026-04-01') TO ('2026-04-16'),
        PARTITION measurements_2026_04_late
            FOR VALUES FROM ('2026-04-16') TO ('2026-05-01')
    );

这解决了一个长期痛点:之前要合并或拆分分区,通常需要创建新分区、拷贝数据、切换、再删除旧分区。现在一条 SQL 搞定,而且是在线操作。

时态数据处理:FOR PORTION OF

PG18 引入了时态约束(PERIOD),PG19 在此基础上增加了 UPDATE ... FOR PORTION OFDELETE ... FOR PORTION OF

-- 创建时态表(PG18+)
CREATE TABLE employee_salary (
    employee_id bigint NOT NULL,
    salary numeric NOT NULL,
    valid_from date NOT NULL,
    valid_to date NOT NULL,
    PERIOD FOR valid_period (valid_from, valid_to),
    EXCLUDE USING gist (employee_id WITH =, valid_period WITH &&)
);

-- 插入薪资记录
INSERT INTO employee_salary VALUES
    (1, 50000, '2026-01-01', '2026-12-31'),
    (2, 60000, '2026-01-01', '2026-06-30');

-- PG19:更新某个时间范围内的薪资
UPDATE employee_salary
SET salary = 55000
FOR PORTION OF valid_period FROM '2026-04-01' TO '2026-09-30'
WHERE employee_id = 1;

-- PG 自动将原始行拆分为三条:
-- 2026-01-01 ~ 2026-04-01:保持 50000
-- 2026-04-01 ~ 2026-09-30:提升到 55000
-- 2026-09-30 ~ 2026-12-31:保持 50000

-- 删除某时间范围内的记录
DELETE FROM employee_salary
FOR PORTION OF valid_period FROM '2026-03-01' TO '2026-05-01'
WHERE employee_id = 2;

需要注意的陷阱

  1. 触发器交互FOR PORTION OF UPDATE 可能会在语句执行过程中创建新行(因为要拆分记录),这会导致行级触发器在这些"新"行上触发——即使这些行在语句开始时并不存在。

  2. 级联外键:时序表上的级联 FK 行为现在更加复杂。在投入生产前一定要充分测试。

  3. 索引使用:确保在 valid_fromvalid_to 列上有合适的 GiST 或 B-tree 索引,以保证时态查询的性能。

查询计划控制:pg_plan_advice 扩展深度解析

查询计划不稳定是 PG DBA 最头疼的问题之一。同一条查询,今天走索引扫描明天走顺序扫描,原因可能是统计信息变化、参数值不同、或者表大小发生了改变。

PG19 引入了 pg_plan_advice 扩展,这是 PG 历史上第一次官方提供**查询计划提示(plan hints)**机制。

安装与使用

-- 创建扩展
CREATE EXTENSION pg_plan_advice;

-- 捕获查询计划建议
SELECT * FROM pg_plan_advice(
    'SELECT * FROM orders WHERE customer_id = 123 AND order_date > $1',
    '{"plan_hints": ["IndexScan(orders, orders_customer_id_idx)"]}'
);

存储与应用建议

-- pg_stash_advice 扩展可以自动应用建议
CREATE EXTENSION pg_stash_advice;

-- 基于查询标识符(queryid)自动加载建议
-- 在 postgresql.conf 中
-- shared_preload_libraries = 'pg_stash_advice'
-- pg_stash_advice.watch_mode = 'auto'

实战场景

-- 场景:某条查询在统计信息更新后计划变差

-- 1. 找到有问题的查询
SELECT queryid, query, calls, mean_exec_time, rows,
       shared_blks_hit, shared_blks_read
FROM pg_stat_statements
WHERE mean_exec_time > 1000  -- > 1秒
ORDER BY total_exec_time DESC
LIMIT 10;

-- 2. 在 PG19 中生成建议
SELECT * FROM pg_plan_advice(
    (SELECT query FROM pg_stat_statements WHERE queryid = 12345),
    '{}'
);

-- 3. 使用 pg_stash_advice 自动应用
-- 建议信息会持久化存储,并在查询计划时自动生效

特别说明:这个特性的引入在社区中经过了长期的辩论。PG 社区一直坚持"查询优化器应该自己做出正确决策"的理念,但现实是复杂查询中的优化器盲点确实是存在的。pg_plan_advice 提供了一种"最后的手段",而不是让默认行为被提示污染。

其他优化器改进

-- 急切聚合(Eager Aggregation):提前聚合减少中间行数
SET enable_eager_aggregate = on;  -- PG19 默认开启

-- 增量排序的更广泛使用
EXPLAIN (ANALYZE)
SELECT department_id, SUM(amount)
FROM transactions
WHERE transaction_date >= '2026-01-01'
GROUP BY department_id, transaction_date
ORDER BY department_id, transaction_date;
-- 优化器现在可能使用增量排序(Incremental Sort)+ 部分聚合

-- IS DISTINCT FROM 优化:当输入不可为空时自动转换为 <> 操作符
-- PG19 之前:IS DISTINCT FROM (总是检查 NULL)
-- PG19 之后:如果列有 NOT NULL 约束,直接使用 <>(更快)

逻辑复制重大改进

序列值的复制

这是逻辑复制用户等待已久的功能。在 PG19 中,序列(SEQUENCE)的值可以通过逻辑复制进行同步:

-- 在发布端
CREATE PUBLICATION my_pub FOR ALL TABLES WITH (publish_via_partition_root = true);
-- 序列数据会自动包含在发布的变更中

-- 在订阅端
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=pub-host dbname=mydb'
PUBLICATION my_pub;
-- 序列值在表同步时会自动同步

这意味着在线升级时,自增主键不会因为升级而出现冲突或空洞——这才是真正可用的在线升级方案。

逻辑复制按需启用

过去,启用逻辑复制需要设置 wal_level = logical 并重启数据库。PG19 允许:

# 将 wal_level 设置为 replica(默认值)
wal_level = replica

然后在需要时动态启用逻辑复制:

-- 创建发布时自动提升 WAL 级别
CREATE PUBLICATION temp_pub FOR TABLE important_table;
-- 系统会自动调整有效 WAL 级别

-- 查看当前有效 WAL 级别
SHOW effective_wal_level;  -- 只读参数,反映实际生效的级别

这个改动的意义在于:你不需要为一个"可能用到"的逻辑复制功能提前付出 WAL 日志量的代价。

发布与订阅的语法增强

-- 发布所有表,排除敏感表
CREATE PUBLICATION public_data
FOR ALL TABLES EXCEPT (credit_cards, passwords, secrets);
-- PG19 新增 EXCEPT 语法

-- 基于外部服务器的订阅(简化凭据管理)
CREATE FOREIGN SERVER primary_db
    FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (host 'primary.example.com', dbname 'mydb');

CREATE SUBSCRIPTION my_sub
SERVER primary_db
PUBLICATION public_data;
-- 凭据通过 user mapping 管理,不在订阅连接字符串中暴露

安全与监控增强

SNI:多证书 TLS

PG19 通过 pg_hosts.conf 实现了基于 SNI(Server Name Indication)的 TLS 证书选择:

# pg_hosts.conf 示例
# hostname      cert_path                     key_path
db1.example.com /etc/ssl/pg/db1.cert.pem      /etc/ssl/pg/db1.key.pem
db2.example.com /etc/ssl/pg/db2.cert.pem      /etc/ssl/pg/db2.key.pem
*.example.com   /etc/ssl/pg/wildcard.cert.pem /etc/ssl/pg/wildcard.key.pem
default         /etc/ssl/pg/default.cert.pem   /etc/ssl/pg/default.key.pem

这在多云架构、SaaS 多租户场景中非常重要。以前想要在同一个 PG 实例上服务多个域名,必须使用通配符证书或 SAN 证书。现在只用 pg_hosts.conf 就能为每个域名配置独立的证书链。

密码过期预警

# postgresql.conf
password_expiration_warning_threshold = 7  -- 默认 7 天

当用户的密码将在指定天数内过期时,客户端会在登录时收到警告消息。这符合企业合规要求(很多公司要求密码每 90 天更换)。

MD5 认证弃用警告

PG19 在成功进行 MD5 认证后向客户端发出弃用警告:

# 控制 MD5 警告行为
md5_password_warnings = on  -- 默认开启

长远来看,所有用户应该迁移到 scram-sha-256。PG19 是 MD5 退役过程中的一个里程碑。

pg_stat_lock:锁统计视图

-- 新视图:按锁类型报告统计
SELECT * FROM pg_stat_lock;

-- 输出示例:
-- lock_type    | granted_count | waited_count | total_wait_time
-- relation     | 284520        | 345          | 1250ms
-- tuple        | 18200         | 23           | 450ms
-- transaction  | 45200         | 12           | 890ms
-- page         | 3200          | 5            | 120ms
-- ...

pg_stat_recovery:恢复过程可见性

-- 查看恢复操作的状态
SELECT * FROM pg_stat_recovery;

-- redo_lsn    | redo_end_lsn | redo_count | redo_bytes | redo_time_ms
-- 0/4A2B100   | 0/5C3D200    | 45201      | 1073741824 | 23450

配合 WAIT FOR LSN 命令,可以实现"读己之写"的最终一致性模式:

-- 在主库获取当前 LSN
SELECT pg_current_wal_lsn();  -- 0/4A2B100

-- 在备库等待指定 LSN 被回放
WAIT FOR LSN '0/4A2B100';
-- 当备库已经回放到这个 LSN 之后,立即返回
-- 然后可以安全地执行 SELECT 查询,保证数据是最新的

-- 结合:主库写入后获取 LSN,传给备库,备库等待 LSN 后再读取

DDL 信息函数

PG19 新增了获取 DDL 信息的函数,简化脚本迁移:

-- 获取重建角色所需的 DDL
SELECT get_role_ddl('my_app_user');
-- CREATE ROLE my_app_user WITH LOGIN PASSWORD '******';
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO my_app_user;

-- 获取重建表空间所需的 DDL
SELECT get_tablespace_ddl('fast_ssd');

-- 获取重建数据库所需的 DDL
SELECT get_database_ddl('my_database');

这对 CI/CD 数据库迁移、环境复制、审计等场景非常有用。

默认配置的静默革命

除了前面提到的 jit = offdefault_toast_compression = lz4 之外,PG19 还有几个值得注意的默认值变更:

# vacuumdb --analyze-only 默认会分析分区表
# 之前:需要手动指定 --partitioned
# PG19:自动包含所有分区

# RADIUS 认证支持已移除
# 如果你使用 RADIUS 认证,需要迁移到 PAM、LDAP 或 SCRAM-SHA-256

在线启用数据校验和

-- PG19 允许在线启用/禁用 data checksums
-- 不再需要 pg_checksums 离线操作或集群重启

ALTER SYSTEM SET data_checksums = on;
SELECT pg_reload_conf();
-- 校验和立即开始对新写入的数据生效

-- 查看状态
SHOW data_checksums;

对于已经在运行的大型集群,这意味着你终于可以在不停机的情况下启用端到端的数据完整性保护。

升级与兼容性指南

关键变化清单

  1. JIT 默认关闭 - OLAP 工作负载需显式开启
  2. TOAST 默认压缩算法改为 LZ4 - 通常透明,但极小众场景可能有影响
  3. RADIUS 认证移除 - 需提前迁移
  4. vacuumdb --analyze-only 行为变更 - 默认分析分区表
  5. 64 位 MultiXact - 纯收益,但建议做回归测试
  6. md5 认证弃用警告 - 建议开始迁移到 scram-sha-256

建议升级路径

PG18 → PG19 推荐步骤:

1. 阅读完整的 Release Notes
   https://www.postgresql.org/docs/19/release-19.html

2. 测试环境验证
   - 部署 PG19 Beta 到 staging 环境
   - 运行完整的功能回归测试
   - 检查 pg_stat_statements 中的查询计划变化

3. 性能基准对比
   - 使用 pgbench 跑 OLTP 负载
   - 使用你的业务查询跑 OLAP 负载
   - 特别关注 JIT 默认值变更的影响

4. 检查扩展兼容性
   - 确保所有扩展已更新到支持 PG19 的版本
   - 特别关注 PostGIS、timescaledb、citus

5. 生产环境升级
   - 使用 pg_upgrade 或逻辑复制
   - 保留回滚计划

总结与展望

PostgreSQL 19 是一个"内外兼修"的版本:

对外:SQL/PGQ 图查询让 PG 进入了一个全新的应用领域。你不再需要一个独立的图数据库来处理关系挖掘任务——一切都在熟悉的 SQL 框架内完成。

对内:64 位 MultiXact、并行自动清理、AIO 自动伸缩、在线数据校验和、REPACK CONCURRENTLY——这些改进的目标只有一个:让 DBA 晚上能睡个好觉。

在 PG 诞生三十年之际,这个版本既是对过去的总结(解决了长期存在的运维痛点),也是对未来的宣言(图查询、时态数据、AI 时代的集成能力)。

如果你还在运行 PG 16 或 17,PG19 的跳跃式改进足以让你认真规划今年的升级路线。如果你已经在 PG 18 上,那么 PG19 的运维体验提升绝对值回升级成本。

Beta 1 已经发布,是时候下载测试,让社区的声音帮助这个版本变得更好。


参考资料

  • PostgreSQL 19 Beta 1 官方公告:https://www.postgresql.org/about/news/postgresql-19-beta-1-released-3313/
  • PG19 发布说明:https://www.postgresql.org/docs/19/release-19.html
  • SQL/PGQ 文档:https://www.postgresql.org/docs/19/ddl-property-graphs.html
  • pg_plan_advice 扩展:https://www.postgresql.org/docs/19/pgplanadvice.html
  • PG19 Open Issues:https://wiki.postgresql.org/wiki/PostgreSQL_19_Open_Items
复制全文 生成海报 PostgreSQL 数据库 图查询 DBA 开源

推荐文章

Vue3中的自定义指令有哪些变化?
2024-11-18 07:48:06 +0800 CST
MySQL 优化利剑 EXPLAIN
2024-11-19 00:43:21 +0800 CST
Vue3中如何实现响应式数据?
2024-11-18 10:15:48 +0800 CST
gin整合go-assets进行打包模版文件
2024-11-18 09:48:51 +0800 CST
程序员茄子在线接单