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 分区表排序慢的问题,这种思路通常非常有效。


相关推荐
Elastic 中国社区官方博客25 分钟前
Elastic 和 Cursor 合作 加速 上下文工程 与 coding agents
大数据·人工智能·elasticsearch·搜索引擎·全文检索
lzhdim1 小时前
SQL 入门 12:SQL 视图:创建、修改与可更新视图
java·大数据·服务器·数据库·sql
科研前沿1 小时前
镜像孪生VS视频孪生核心技术产品核心优势
大数据·人工智能·算法·重构·空间计算
2301_795099741 小时前
让 CSS Grid 自适应容器尺寸的动态布局方案
jvm·数据库·python
FQNmxDG4S2 小时前
Maven依赖管理:版本冲突解决与生命周期控制
java·数据库·maven
热爱运维的小七2 小时前
告别内存溢出:ActiveMQ 性能诊断与全流程优化
数据库·it运维·activemq·devops
@小柯555m2 小时前
MySql(高级操作符--操作符混合运用)
数据库·sql·mysql
CDN3602 小时前
排查实录:网站偶发502/504错误?360CDN回源超时配置与日志分析技巧
前端·数据库
bzmK1DTbd2 小时前
JDBC编程规范:PreparedStatement与事务管理
数据库·python·eclipse
lizhihai_992 小时前
股市学习心得-六张分时保命图
大数据·人工智能·学习