PostgreSQL 分区表排序优化:Append Sort 优化为 Merge Append

最近在优化一个 PostgreSQL 查询时,遇到一个典型的 分区表排序性能问题,通过索引重构和执行计划分析,,优化效果非常明显,记录一下完整过程。


一、问题背景

业务中有一张 按状态分区的表

复制代码
order_partitioned_table
├── partition_active      (status = 0)
└── partition_archive     (default)

查询 SQL 如下:

复制代码
SELECT user_id, object_id
FROM order_partitioned_table
WHERE status IN (0, 22)
  AND expire_time < ?
ORDER BY expire_time ASC, object_id ASC;

该查询返回 60W+ 行数据,执行计划如下:

复制代码
Append
  -> Index Scan partition_active
  -> Index Scan partition_archive
Sort

执行时间:

复制代码
Execution Time: 53s

明显瓶颈在:

复制代码
Append + Sort

二、为什么会慢?

执行流程是:

  1. 扫描 partition_active

  2. 扫描 partition_archive

  3. 拼接结果 (Append)

  4. 对 60W 行整体排序 (Sort)

也就是:

复制代码
Append
   ↓
Sort (600k rows)

这种模式的问题:

  • 排序数据量大

  • CPU 消耗高

  • 内存消耗大

  • 可能发生磁盘排序


三、理想执行计划

我们希望变成:

复制代码
Merge Append

而不是:

复制代码
Append + Sort

为什么?

Append + Sort

相当于:

  1. 两堆乱序数据合并

  2. 再整体排序

复杂度:

复制代码
O(N log N)

Merge Append

前提:

两个子查询 已经有序

然后:

复制代码
归并排序

复杂度:

复制代码
O(N)

这就是优化核心。


四、优化思路

关键点:

每个分区本身就按 ORDER BY 顺序输出

ORDER BY:

复制代码
expire_time, object_id

因此索引必须:

复制代码
(expire_time, object_id)

并且:

复制代码
WHERE status IN (0,22)

因此采用 Partial Index


五、最终优化索引

Active 分区

复制代码
CREATE INDEX CONCURRENTLY partition_active_exp_idx
ON partition_active (expire_time, object_id, user_id)
WHERE status IN (0, 22);

Archive 分区

复制代码
CREATE INDEX CONCURRENTLY partition_archive_exp_idx
ON partition_archive (expire_time, object_id, user_id)
WHERE status IN (0, 22);

六、优化后的执行计划

复制代码
Merge Append
   -> Index Only Scan partition_active
   -> Index Only Scan partition_archive

执行时间:

复制代码
Execution Time: 559 ms

优化效果:

阶段 执行时间
初始 53s
优化后 0.55s

提升:

复制代码
≈ 100x 性能提升

七、为什么 Partial Index 更适合

原索引:

复制代码
(status, user_id, expire_time, ...)

问题:

  • status 放在前面破坏排序

  • 无法使用 Merge Append

而 Partial Index:

复制代码
(expire_time, object_id)
WHERE status IN (0,22)

优势:

  • 索引更小

  • 顺序更符合 ORDER BY

  • 更容易触发 Merge Append


八、Merge Append vs Append + Sort

Append + Sort

流程:

复制代码
partition_active
partition_archive
      ↓
    Append
      ↓
     Sort

需要:

  • 全量排序

  • 大内存

  • CPU 高


Merge Append

流程:

复制代码
partition_active (ordered)
partition_archive (ordered)
      ↓
   Merge Append

优势:

  • 不需要整体排序

  • 只做归并

  • 更快更稳定


九、PostgreSQL 版本注意事项

该优化在 PostgreSQL 14 测试通过。

PostgreSQL 14 对分区优化能力相比 15/16 略弱,因此:

  • 索引顺序尤为重要

  • Partial Index 更容易触发 Merge Append

在 PostgreSQL 15+ 中,该优化效果通常更明显。


十、总结

本次优化关键点:

  1. 避免 Append + Sort

  2. 让每个分区按排序键输出

  3. 使用 Partial Index

  4. 触发 Merge Append

最终实现:

复制代码
53s → 559ms

分区表排序优化,核心就是:

让每个分区先排好,再合并,而不是先合并再排序


如果你也遇到 PostgreSQL 分区表排序慢的问题,这种思路通常非常有效。


相关推荐
迷枫7122 小时前
达梦数据库的体系架构
数据库·oracle·架构
夜晚打字声2 小时前
9(九)Jmeter如何连接数据库
数据库·jmeter·oracle
Chasing__Dreams3 小时前
Mysql--基础知识点--95--为什么避免使用长事务
数据库·mysql
safestar20123 小时前
ES批量写入性能调优:BulkProcessor 参数详解与实战案例
java·大数据·运维·jenkins
weixin_156241575763 小时前
基于YOLOv8深度学习花卉识别系统摄像头实时图片文件夹多图片等另有其他的识别系统可二开
大数据·人工智能·python·深度学习·yolo
NineData3 小时前
NineData 智能数据管理平台新功能发布|2026 年 3 月
数据库·oracle·架构·dba·ninedata·数据复制·数据迁移工具
小陈工3 小时前
2026年4月7日技术资讯洞察:下一代数据库融合、AI基础设施竞赛与异步编程实战
开发语言·前端·数据库·人工智能·python
❀͜͡傀儡师3 小时前
k8s部署的Nexus 3 数据库损坏恢复指南:从删除损坏数据库到完整数据重建
数据库·kubernetes·nexus3
科技与数码3 小时前
互联网保险迎来新篇章,元保方锐分享行业发展前沿洞察
大数据·人工智能