编程 PostgreSQL 19 深度解析:从原生图查询到执行计划锁定——2026年最值得升级的数据库版本全景指南

2026-05-17 06:43:11 +0800 CST views 5

PostgreSQL 19 深度解析:从原生图查询到执行计划锁定——2026年最值得升级的数据库版本全景指南

引言:为什么 PG 19 值得你认真关注

2026年4月8日,PostgreSQL 19 正式进入特性冻结(Feature Freeze)阶段。所有新功能已锁定,开发重心全面转向稳定化测试与 Bug 修复。目标发布时间为 2026 年 9 月,Beta 测试预计于 5 月启动。随着 Bruce Momjian 完成 PG 19 发布说明的首个草稿,这个年度重磅版本的全貌正逐渐清晰。

如果你一直在用 PostgreSQL,你可能已经习惯了它的"慢热"风格——不像某些数据库动辄搞"革命性重构",PG 更像是稳扎稳打的老工程师,每年解决几个长期痛点,积少成多。但 PG 19 不同。这一版本的改动密度和质量都超出了往年:SQL/PGQ 图查询、pg_plan_advice 执行计划锁定、MERGE/SPLIT PARTITIONS、动态 WAL 级别调整、REPACK 内核化、时态表……几乎每一个特性都直击生产环境的核心痛点。

本文将从程序员视角出发,逐一拆解 PG 19 的核心新特性,配合代码示例和架构分析,帮你判断什么时候该升级、怎么升级、升级后怎么用。


一、SQL/PGQ 图查询:关系型数据库的"图思维"革命

1.1 什么是 SQL/PGQ

SQL/PGQ(Property Graph Queries)是 SQL:2023 标准中引入的属性图查询规范。它允许你在关系型数据库中,用声明式的 SQL 语法执行图遍历操作。在此之前,如果你想在 PostgreSQL 里做图查询,通常需要安装 Apache AGE 扩展、使用 AgensGraph 分支,或者借助 pgRouting 算法库。

PG 19 把图查询能力直接放进了内核。不需要装扩展,不需要改分支,CREATE TABLE 建表,SELECT 查询,一切如常——只不过你的 SQL 里可以写图遍历了。

1.2 核心概念速览

属性图由三类元素组成:

  • 图(Graph):一组顶点和边的集合
  • 顶点(Vertex):带有属性标签的实体,比如"用户""商品"
  • 边(Edge):带有属性标签的连接关系,比如"购买了""关注了"

PG 19 引入了 CREATE PROPERTY GRAPH 语法来定义图:

-- 定义一个社交网络属性图
CREATE PROPERTY GRAPH social_network
  VERTEX TABLES (
    users LABEL user
      PROPERTIES (id, name, email, created_at),
    posts LABEL post
      PROPERTIES (id, user_id, title, content, published_at)
  )
  EDGE TABLES (
    follows LABEL follows
      SOURCE KEY (follower_id) REFERENCES users (id)
      DESTINATION KEY (followee_id) REFERENCES users (id)
      PROPERTIES (created_at),
    authored LABEL authored
      SOURCE KEY (user_id) REFERENCES users (id)
      DESTINATION KEY (post_id) REFERENCES posts (id)
      PROPERTIES (created_at)
  );

然后就可以用 MATCH 语法执行图遍历了:

-- 查找"张三关注的人中,谁也关注了李四"(二度关系)
SELECT n.name AS mutual_follow
FROM GRAPH_TABLE (
  social_network
  MATCH (a:users)-[e1:follows]->(b:users)-[e2:follows]->(c:users)
  WHERE a.name = '张三' AND c.name = '李四'
  COLUMNS (b.name)
) AS n;

1.3 实际应用场景

场景一:知识图谱查询

假设你有一套企业内部的知识库,实体包括文档、作者、标签、部门,关系包括"撰写""归属""引用"。用传统 SQL 查"某个作者所在部门的所有文档中被引用最多的前 5 篇",你需要多层递归 CTE,可读性和性能都不理想:

-- 传统写法:递归CTE,复杂且难以维护
WITH RECURSIVE dept_docs AS (
  SELECT d.id, d.title
  FROM documents d
  JOIN authors a ON d.author_id = a.id
  JOIN departments dept ON a.dept_id = dept.id
  WHERE dept.name = '工程部'
),
citation_chain AS (
  SELECT d.id, d.title, COUNT(*) AS citation_count
  FROM documents d
  JOIN citations c ON c.target_id = d.id
  WHERE c.source_id IN (SELECT id FROM dept_docs)
  GROUP BY d.id, d.title
)
SELECT title, citation_count
FROM citation_chain
ORDER BY citation_count DESC
LIMIT 5;

用图查询,一行 MATCH 就够了:

SELECT doc.title, COUNT(*) AS citations
FROM GRAPH_TABLE (
  knowledge_graph
  MATCH (dept:departments {name: '工程部'})<-[:belongs_to]-(author:authors)
        -[:wrote]->(source:documents)-[:cites]->(target:documents)
  COLUMNS (target.title)
) AS doc
GROUP BY doc.title
ORDER BY citations DESC
LIMIT 5;

场景二:金融反欺诈——资金链路追踪

银行系统中最常见的反欺诈场景:追踪一笔资金从 A 到 B 到 C 的多层转账链路,判断是否存在洗钱或欺诈行为:

-- 追踪从可疑账户出发的所有3层资金转移路径
SELECT path.*
FROM GRAPH_TABLE (
  financial_graph
  MATCH path = (src:accounts {id: 'ACC_SUSPECT_001'})
               -[t1:transfer*1..3]->(dst:accounts)
  COLUMNS (
    src.id AS source,
    dst.id AS destination,
    ELEMENT_ID(t1) AS transfer_id
  )
) AS path
WHERE dst.risk_score > 0.8;

*1..3 是可变长度路径匹配,表示 1 到 3 层转账。这在传统 SQL 中几乎无法用一句查询实现。

场景三:与 pgvector 的"语义+关系"双轮驱动

这是 PG 19 图查询最令人兴奋的组合场景。pgvector 提供向量检索能力("找到语义相似的文档"),图查询提供关系遍历能力("找到这个文档作者的所有合作者")。两者结合:

-- 先用向量相似度找到相关文档,再用图查询找到其作者的社交关系网
WITH similar_docs AS (
  SELECT id, title, embedding <=> '[0.1, 0.2, ...]' AS distance
  FROM documents
  ORDER BY embedding <=> '[0.1, 0.2, ...]'
  LIMIT 10
)
SELECT DISTINCT author.name, author.email
FROM GRAPH_TABLE (
  knowledge_graph
  MATCH (doc:documents)-[:authored_by]->(author:authors)
       -[:follows]->(colleague:authors)
  WHERE doc.id IN (SELECT id FROM similar_docs)
  COLUMNS (author.name, author.email, colleague.name AS collaborator)
) AS result;

这种"语义搜索 + 图遍历"的组合,在推荐系统、智能客服、知识库等场景中价值巨大。

1.4 性能考量

图查询的性能取决于数据量和遍历深度。对于百万级顶点的图,单层遍历通常在毫秒级;三层以上遍历可能需要索引辅助。建议在关键属性上创建 B-tree 索引,在大规模图场景中考虑结合 BRIN 索引降低索引维护开销。


二、pg_plan_advice:执行计划的"终极掌控"

2.1 一个真实的血泪教训

想象这个场景:你有一条跑了三年的核心 SQL,查询耗时稳定在 5ms。某天 DBA 做了一次例行 ANALYZE,统计信息更新后,查询规划器突然选择了 Nested Loop 代替原来的 Hash Join,查询耗时暴涨到 30 秒。线上 P0 事故。

这不是假设,而是很多 PostgreSQL 用户的真实经历。执行计划不稳定是 PostgreSQL 最令人头疼的问题之一——规划器很聪明,但它太敏感了。统计信息的微小变化、版本升级后的成本模型调整、甚至是数据分布的渐变,都可能导致执行计划"突变"。

2.2 旧方案的痛点

在 PG 19 之前,DBA 的应对手段有限:

  1. pg_hint_plan:第三方插件,通过注释注入提示。但它通过 Hook 机制修改规划器内部状态,代码量大(约 2500 行),维护风险高,与 PG 版本升级的兼容性经常出问题。
  2. 重写 SQL:用 SET LOCAL 强制参数、用子查询引导连接顺序……维护成本极高。
  3. 调整全局参数:比如修改 work_memrandom_page_cost,但这是全局影响,改了这个优化了那个,按下葫芦浮起瓢。

2.3 PG 19 的解决方案:路径生成策略

PG 19 引入了**路径生成策略(Path Generation Strategies)**机制——这是查询规划器底层的架构级改进。

传统规划器的工作流程:

生成多条候选路径 → 成本估算 → 淘汰劣质路径 → 选择最优

问题在于,规划器可能在成本比较的早期就丢弃了某些路径,扩展插件即使想"强制保留"也无能为力。路径生成策略机制让插件可以在规划器最早阶段就影响路径生成决策,从根本上解决了这个问题。

2.4 pg_plan_advice 实战

pg_plan_advice 作为 contrib 模块随 PG 19 一同发布,提供原生的执行计划建议能力。

第一步:获取当前执行计划的建议字符串

-- 加载扩展
LOAD 'pg_plan_advice';

-- 查看当前查询的执行计划建议
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT f.id, f.amount, d.category, d.region
FROM orders_fact f
JOIN dim_product p ON f.product_id = p.id
JOIN dim_region d ON p.region_id = d.id
WHERE d.region = '华东'
  AND f.order_date >= '2026-01-01'
ORDER BY f.amount DESC
LIMIT 100;

输出示例:

QUERY PLAN
----------------------------------------------
Limit
  -> Sort
     -> Hash Join
        Hash Cond: (f.product_id = p.id)
        -> Seq Scan on orders_fact f
           Filter: (order_date >= '2026-01-01')
        -> Hash
           -> Hash Join
              Hash Cond: (p.region_id = d.id)
              -> Seq Scan on dim_product p
              -> Hash
                 -> Seq Scan on dim_region d
                    Filter: (region = '华东')

Generated Plan Advice:
JOIN_ORDER(f p d) HASH_JOIN(p) HASH_JOIN(d) SEQ_SCAN(f p d) NO_GATHER(f p d)

第二步:理解建议字符串

建议项含义
JOIN_ORDER(f p d)指定连接顺序:f 为驱动表,依次关联 p 和 d
HASH_JOIN(p)对 p 表使用 Hash Join
HASH_JOIN(d)对 d 表使用 Hash Join
SEQ_SCAN(f p d)对三张表均使用顺序扫描
NO_GATHER(f p d)不将三张表置于并行 Gather 节点下

第三步:应用建议

-- 应用完整建议
SET pg_plan_advice.advice = 'JOIN_ORDER(f p d) HASH_JOIN(p) HASH_JOIN(d) SEQ_SCAN(f p d) NO_GATHER(f p d)';

-- 或者只锁定连接顺序,其他交给规划器
SET pg_plan_advice.advice = 'JOIN_ORDER(f p d)';

-- 验证效果
EXPLAIN ANALYZE
SELECT f.id, f.amount, d.category, d.region
FROM orders_fact f
JOIN dim_product p ON f.product_id = p.id
JOIN dim_region d ON p.region_id = d.id
WHERE d.region = '华东'
  AND f.order_date >= '2026-01-01'
ORDER BY f.amount DESC
LIMIT 100;

第四步:持久化配置

-- 在 postgresql.conf 中全局配置
# pg_plan_advice.advice = 'JOIN_ORDER(f p d)'

-- 或者针对特定数据库/用户配置
ALTER DATABASE analytics SET pg_plan_advice.advice = 'JOIN_ORDER(f p d) HASH_JOIN(p)';

2.5 与 pg_hint_plan 的区别

特性pg_hint_plan(旧)pg_plan_advice(新)
来源第三方插件官方 contrib 模块
注入方式SQL 注释(/*+ Hint */GUC 参数
底层机制Hook 修改内部状态路径生成策略(规划器原生)
代码量~2500 行大幅精简
版本兼容性经常需要适配随 PG 版本发布
作用域单条 SQL会话/数据库/全局

注意:pg_plan_advice 目前是"建议"模式,不是强制。如果建议的路径规划器无法生成,它会自动回退到默认行为。这比 pg_hint_plan 的"强制"模式更安全,不会因为不合理的提示导致查询直接报错。


三、并行自动清理(Parallel Autovacuum):不再让大表清理成为噩梦

3.1 问题背景

Autovacuum 是 PostgreSQL 的自动清理机制,负责回收死元组(Dead Tuples)、更新统计信息、冻结事务 ID。对于一张千万级的大表,单线程清理可能耗时数小时甚至更久。在清理期间,如果死元组累积速度超过清理速度,表会持续膨胀,查询性能下降。

更关键的是:清理不及时会导致事务 ID 回卷风险——PostgreSQL 的 MVCC 机制依赖事务 ID,如果最旧的事务 ID 太接近当前值,数据库会强制进入只读模式来防止数据损坏。这可是 P0 级事故。

3.2 PG 19 的并行清理

PG 19 引入了 Parallel Autovacuum,允许多个 Worker 并行清理同一张表的不同数据块。

-- 全局限制并行清理 Worker 数量
SET autovacuum_max_parallel_workers = 4;

-- 针对特定大表启用并行清理
ALTER TABLE orders_fact SET (autovacuum_parallel_workers = 4);

关键配置参数:

-- 查看当前并行清理配置
SHOW autovacuum_max_parallel_workers;  -- 集群级上限,默认关闭
SHOW max_parallel_workers;              -- 全局并行 Worker 池

-- 针对单表配置
ALTER TABLE huge_table SET (
  autovacuum_parallel_workers = 2,      -- 使用 2 个并行 Worker
  autovacuum_vacuum_cost_limit = 2000,  -- 每个 Worker 的成本限制
  autovacuum_vacuum_cost_delay = '2ms'  -- 成本超限后的延迟
);

3.3 一个重要提醒:并行 ≠ 总是更快

这是最容易踩的坑。并行清理的 Worker 来自全局并行 Worker 池(max_parallel_workers),如果你的系统已经在运行并行查询,额外的清理 Worker 会抢占 CPU 资源。对于 I/O 密集型的清理场景(比如表数据在机械硬盘上),增加 Worker 可能毫无帮助,甚至因资源争抢而变慢。

最佳实践:

-- 1. 默认关闭并行清理(官方推荐)
autovacuum_max_parallel_workers = 0

-- 2. 只对真正需要的大表启用
ALTER TABLE analytics_events SET (autovacuum_parallel_workers = 2);

-- 3. 在业务低峰期调度
-- 可以通过 pg_cron 扩展在凌晨手动触发
SELECT cron.schedule(
  'vacuum_large_tables',
  '0 3 * * 0',  -- 每周日凌晨3点
  $$SELECT pg_catalog.vacuum(analyze => true, verbose => false, 
    table_name := 'analytics_events', 
    parallel_workers => 2)$$
);

四、MERGE/SPLIT PARTITIONS:分区管理的"平民化"革命

4.1 旧方案的痛苦

假设你有一个按月份分区的订单表,年底需要把 12 个月份分区合并为 4 个季度分区。在 PG 19 之前,你需要:

-- 旧方案:手动创建、迁移、删除,约20行SQL
BEGIN;
CREATE TABLE orders_q1 (LIKE orders INCLUDING DEFAULTS);
INSERT INTO orders_q1 SELECT * FROM orders_2026_01;
INSERT INTO orders_q1 SELECT * FROM orders_2026_02;
INSERT INTO orders_q1 SELECT * FROM orders_2026_03;
DROP TABLE orders_2026_01;
DROP TABLE orders_2026_02;
DROP TABLE orders_2026_03;
ALTER TABLE orders ATTACH PARTITION orders_q1
  FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');
COMMIT;

容易出错、需要手动处理约束、期间还要持有锁。

4.2 PG 19 的新方案

-- 一行命令搞定
ALTER TABLE orders MERGE PARTITIONS (
  orders_2026_01, orders_2026_02, orders_2026_03
) INTO orders_q1;

-- 拆分也同样简单
ALTER TABLE orders SPLIT PARTITION orders_q1 INTO (
  PARTITION orders_2026_01 FOR VALUES FROM ('2026-01-01') TO ('2026-02-01'),
  PARTITION orders_2026_02 FOR VALUES FROM ('2026-02-01') TO ('2026-03-01'),
  PARTITION orders_2026_03 FOR VALUES FROM ('2026-03-01') TO ('2026-04-01')
);

4.3 实战场景:动态分区生命周期管理

-- 按日分区的日志表
CREATE TABLE access_logs (
  id BIGSERIAL,
  path TEXT,
  method TEXT,
  status_code INT,
  response_time FLOAT,
  created_at TIMESTAMPTZ DEFAULT now()
) PARTITION BY RANGE (created_at);

-- 自动创建每日分区(配合 pg_partman)
-- 30天后合并为月度分区
ALTER TABLE access_logs MERGE PARTITIONS (
  access_logs_2026_05_01, access_logs_2026_05_02, 
  access_logs_2026_05_03, access_logs_2026_05_04,
  access_logs_2026_05_05, access_logs_2026_05_06,
  access_logs_2026_05_07
) INTO access_logs_2026_05_w1;

-- 3个月后合并为季度分区
ALTER TABLE access_logs MERGE PARTITIONS (
  access_logs_2026_01, access_logs_2026_02, access_logs_2026_03
) INTO access_logs_2026_q1;

-- 1年后归档到历史分区
ALTER TABLE access_logs MERGE PARTITIONS (
  access_logs_2025_q1, access_logs_2025_q2, 
  access_logs_2025_q3, access_logs_2025_q4
) INTO access_logs_2025;

注意: 目前 MERGE/SPLIT PARTITIONS 会在整个执行期间对父表加排他锁。建议在业务低峰期执行,或配合 pg_background 扩展在后台运行。


五、动态 WAL 级别调整:告别"重启恐惧症"

5.1 旧痛点

这是困扰 PostgreSQL CDC(Change Data Capture)用户多年的问题。要使用逻辑复制或逻辑解码(比如 Debezium、wal2json),你必须在 postgresql.conf 中设置:

wal_level = logical

然后重启数据库实例。即便你只有一个逻辑复制槽、每天只同步几百条数据,整个数据库也要一直承担 logical 级别 WAL 的额外开销——更多的 I/O、更多的存储空间。很多 DBA 不敢开逻辑解码,就是因为这个"永久性"的性能影响。

5.2 PG 19 的解决方案

PG 19 实现了 WAL 级别的动态切换

-- 创建第一个逻辑复制槽 → WAL 级别自动提升为 logical
CREATE_REPLICATION_SLOT my_slot LOGICAL pgoutput;

-- 确认当前有效 WAL 级别
SHOW effective_wal_level;  -- logical

-- 删除最后一个逻辑复制槽 → WAL 级别自动降回 replica
DROP_REPLICATION_SLOT my_slot;

SHOW effective_wal_level;  -- replica

effective_wal_level 是新增的只读 GUC 参数,实时反映当前实际生效的 WAL 级别。

5.3 对 CDC 用户的意义

# Debezium / CDC 场景:按需开启逻辑解码
import psycopg2

conn = psycopg2.connect("dbname=production")
conn.autocommit = True
cur = conn.cursor()

# 需要同步时创建槽
cur.execute("CREATE_REPLICATION_SLOT cdc_slot LOGICAL pgoutput")

# 同步完成后删除槽,WAL 开销自动消失
cur.execute("DROP_REPLICATION_SLOT cdc_slot")

# 也可以结合定时任务:每天同步时开启,同步完关闭

这对间歇性 CDC 场景(如每天凌晨全量同步增量数据)是一个重大改进。


六、REPACK 内核化:表膨胀治理进入新时代

6.1 pg_repack 的前世今生

pg_repack 一直是 PostgreSQL 社区最受欢迎的第三方运维工具之一。它通过创建临时表、在后台复制数据、最后交换表名的方式,实现了"在线重整表数据"的能力——相比于 VACUUM FULL 的排他锁(会阻塞所有读写),pg_repack 只需要短暂的 Access Exclusive 锁来交换表名。

但它毕竟是第三方工具,安装配置复杂、版本兼容性风险高、出问题时排查困难。

6.2 PG 19 的 REPACK 命令

-- 基础模式:等价于 VACUUM FULL
REPACK orders_fact;

-- 索引排序模式:等价于 CLUSTER
REPACK orders_fact USING INDEX idx_orders_created_at;

-- 查看进度
SELECT * FROM pg_stat_progress_repack;

pg_stat_progress_repack 输出示例:

phaseheap_blks_scannedheap_blks_writtenindex_rebuild_progress
seqscan12500000-
rewrite1250000875000idx_orders: 67%
index_build12500001250000idx_orders: 100%
swap---

6.3 实战:表膨胀检测与治理

-- 1. 检测表膨胀
SELECT 
  schemaname || '.' || relname AS table_name,
  pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
  n_dead_tup,
  ROUND(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_ratio_pct
FROM pg_stat_user_tables
WHERE n_dead_tup > 10000
ORDER BY n_dead_tup DESC
LIMIT 10;

-- 2. 对膨胀严重的表执行 REPACK
REPACK bloated_table;

-- 3. 验证效果
SELECT 
  pg_size_pretty(pg_total_relation_size('bloated_table')) AS size_after_repack;

七、时态表:SQL:2023 的精准时间旅行

7.1 什么是时态表

时态表(Temporal Table)是 SQL:2023 标准引入的特性,允许你记录数据的历史版本,并查询"某个时间点的数据状态"。PG 19 实现了 UPDATE/DELETE FOR PORTION OF 语法。

7.2 实战示例

-- 创建带时间维度的合同表
CREATE TABLE contracts (
  id SERIAL PRIMARY KEY,
  customer_id INT NOT NULL,
  product TEXT NOT NULL,
  price NUMERIC(10, 2) NOT NULL,
  valid_from DATE NOT NULL,
  valid_to DATE,
  CHECK (valid_from <= COALESCE(valid_to, '9999-12-31'))
);

-- 插入数据
INSERT INTO contracts (customer_id, product, price, valid_from)
VALUES (1, 'Pro Plan', 99.00, '2026-01-01');

-- 部分更新:只修改2026-04-01之后的价格
UPDATE contracts
FOR PORTION OF valid_period FROM '2026-04-01' TO '9999-12-31'
SET price = 129.00
WHERE customer_id = 1 AND product = 'Pro Plan';

-- 结果:原记录的 valid_to 被设为 '2026-03-31',
--       新增一条 valid_from='2026-04-01', price=129.00 的记录

-- 查询某时间点的生效价格
SELECT * FROM contracts
WHERE customer_id = 1 
  AND product = 'Pro Plan'
  AND valid_from <= '2026-03-15'
  AND (valid_to IS NULL OR valid_to > '2026-03-15');

这对于合同管理、费率变更、法规审计等需要"时间溯源"的场景非常有用。


八、其他值得关注的改进

8.1 GROUP BY ALL:告别手写分组列

-- 旧写法
SELECT region, category, SUM(amount), AVG(amount)
FROM orders
GROUP BY region, category;

-- 新写法:自动按 SELECT 中所有非聚合列分组
SELECT region, category, SUM(amount), AVG(amount)
FROM orders
GROUP BY ALL;

简单但实用,减少了 GROUP BY 列表与 SELECT 列表不一致的低级错误。

8.2 EXPLAIN ANALYZE 性能提升

PG 19 使用 RDTSC(Read Time-Stamp Counter)指令替代传统的 clock_gettime() 来计时。在高频执行 EXPLAIN ANALYZE 的场景下,计时开销从微秒级降至纳秒级。这对于性能基准测试和自动化的执行计划监控场景意义重大。

-- 现在可以放心地在监控系统中频繁执行 EXPLAIN ANALYZE
-- 不用担心计时本身影响查询性能
EXPLAIN (ANALYZE, BUFFERS, TIMING)
SELECT COUNT(*) FROM orders WHERE order_date > '2026-01-01';

8.3 file_fdw 增强

-- 导入 CSV 文件时跳过前两行说明文字
CREATE FOREIGN TABLE raw_data (
  id INT,
  name TEXT,
  value NUMERIC
) SERVER file_server
OPTIONS (filename '/data/import.csv', format 'csv', 
         header 'false', skip_lines '2');

8.4 逻辑发布精细控制

-- 发布所有表,但排除临时表和日志表
CREATE PUBLICATION main_pub
FOR ALL TABLES
EXCEPT TABLE temp_staging, access_logs, audit_trail;

-- 后续追加排除
ALTER PUBLICATION main_pub EXCEPT TABLE internal_metrics;

九、性能优化指南

9.1 升级前的性能基线测试

-- 1. 导出当前统计信息(PG 19 新功能)
pg_dump --statistics-only -d production > pre_upgrade_stats.sql

-- 2. 记录关键查询的执行计划和耗时
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT ... -- 你的核心查询
\g /tmp/baseline_plan.json

-- 3. 记录表大小和索引使用情况
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS size
FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;

9.2 升级后的对比验证

-- 1. 恢复统计信息,确保规划器做出相同决策
\i /tmp/pre_upgrade_stats.sql

-- 2. 再次执行核心查询,对比执行计划和耗时
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT ... -- 相同的核心查询
\g /tmp/post_upgrade_plan.json

-- 3. 使用 pg_plan_advice 锁定关键查询的执行计划
SET pg_plan_advice.advice = '...';

十、总结与升级建议

PG 19 的定位是"实用性版本"——不是颠覆性革命,而是对长期痛点的精准修复。以下是按优先级的升级建议:

立即升级(强烈推荐):

  • 如果你的系统受困于执行计划不稳定 → pg_plan_advice 是救星
  • 如果你正在使用 pg_repack → 迁移到原生 REPACK
  • 如果你有大规模分区表 → MERGE/SPLIT PARTITIONS 大幅降低运维成本

评估后升级:

  • 如果你需要图查询能力 → SQL/PGQ 免安装开箱即用
  • 如果你在做 CDC → 动态 WAL 级别调整消除运维顾虑
  • 如果你有审计/合同管理需求 → 时态表值得研究

暂不升级:

  • 如果你的 PG 版本还在 14 以下 → 先升级到 17 或 18
  • 如果你的应用对稳定性要求极高 → 等 9 月 GA 后再评估

PG 19 代表了 PostgreSQL 从"一个优秀的关系型数据库"向"一个统一的多模数据平台"的关键跃迁。向量检索、全文搜索、图查询、时态数据——这些曾经分散在不同系统中的能力,正在被统一到同一套 SQL 语法和运维体系之下。对于追求"少折腾、多办事"的工程师来说,这无疑是个好消息。

2026年9月,值得期待。

推荐文章

Requests库详细介绍
2024-11-18 05:53:37 +0800 CST
mysql 计算附近的人
2024-11-18 13:51:11 +0800 CST
Golang实现的交互Shell
2024-11-19 04:05:20 +0800 CST
Vue3中如何处理组件的单元测试?
2024-11-18 15:00:45 +0800 CST
介绍 Vue 3 中的新的 `emits` 选项
2024-11-17 04:45:50 +0800 CST
mysql 优化指南
2024-11-18 21:01:24 +0800 CST
Vue3结合Driver.js实现新手指引功能
2024-11-19 08:46:50 +0800 CST
php使用文件锁解决少量并发问题
2024-11-17 05:07:57 +0800 CST
Linux 网站访问日志分析脚本
2024-11-18 19:58:45 +0800 CST
如何在Vue3中定义一个组件?
2024-11-17 04:15:09 +0800 CST
Vue3中如何进行异步组件的加载?
2024-11-17 04:29:53 +0800 CST
支付宝批量转账
2024-11-18 20:26:17 +0800 CST
thinkphp swoole websocket 结合的demo
2024-11-18 10:18:17 +0800 CST
程序员茄子在线接单