关于 YMatrix
部署数据库
使用数据库
管理集群
最佳实践
高级功能
高级查询
联邦查询
Grafana 监控
备份恢复
灾难恢复
图数据库
管理手册
性能调优
故障诊断
工具指南
系统配置参数
SQL 参考
本文介绍 YMatrix 的增量排序能力,包括它的使用条件、配置方式、执行计划识别方法,以及不生效时如何排查。
适用版本:YMatrix 6.8.6 及以上
普通排序需要读取全部输入后再排序。当输入本身已经按查询需要的部分排序键有序时,增量排序会把输入按这个已排序前缀切成若干组,只对组内剩余键排序,从而减少比较次数和排序需要占用的内存。
YMatrix 的增量排序由两层配合完成:
| 层次 | 作用 |
|---|---|
| PostgreSQL Planner | 判断输入是否提供了可用的排序前缀,决定是否生成 Incremental Sort 节点 |
| 向量化执行器 | 把满足条件的增量排序转换为 MxVSort 的 SubKeySorter 实现,按批次处理数据 |
Planner 决定"要不要用增量排序",向量化执行器决定"用哪种实现"。两者都通过,才会得到向量增量排序。
增量排序要生效,需要同时满足:
ORDER BY,且排序键中至少有一个键已经被输入计划保证有序。NULLS FIRST / NULLS LAST 以及 collation。有序前缀的常见来源有:索引顺序、MARS3 表的物理排序键,以及下层排序节点输出的顺序。表上建有索引不等于上层排序一定会用到该索引的顺序,必须以实际执行计划为准。
增量排序由两个参数控制,默认都开启:
| 参数 | 默认值 | 作用 |
|---|---|---|
enable_incremental_sort |
on |
是否允许 Planner 生成 Incremental Sort 计划 |
matrix.enable_mxv_subkey_sort |
on |
是否允许 MxVSort 使用 SubKeySorter,以及原生增量排序是否允许转换为向量增量排序 |
matrix.enable_mxv_subkey_sort 在 6.8 中已存在并默认开启;6.8.6 新增的是 Planner 生成增量排序计划的能力(enable_incremental_sort),以及把生成的原生增量排序转换为向量增量排序。
常用组合如下(组合前三项已假设向量化执行与向量排序已开启):
| 使用目的 | enable_incremental_sort |
matrix.enable_mxv_subkey_sort |
预期行为 |
|---|---|---|---|
| 允许向量增量排序 | on |
on |
条件满足时使用向量增量排序;无法向量化时保留原生增量排序 |
| 使用原生增量排序,仍允许向量扫描 | on |
off |
排序节点保留原生 Incremental Sort,下层扫描仍可向量化 |
| 关闭增量排序 | off |
off |
不生成 Incremental Sort,普通排序仍可向量化 |
日常使用前,先确认使用 PostgreSQL Planner 并开启向量化:
SET optimizer = off;
SET matrix.enable_mxvector = on;
SET matrix.enable_mxv_sort = on;
再按需选择上表中的一组配置。也可以使用 SET LOCAL,在事务结束时自动恢复。
注意!
enable_incremental_sort=off不能单独用于关闭所有向量增量排序。普通MxVSort在特定输入下仍会识别下层提供的排序前缀,详见限制与注意事项。
以下示例创建一个 MARS3 表,用 mars3_btree 索引提供一个排序前缀,然后比较不同配置下的执行计划。示例使用事务包裹,结束时 ROLLBACK 即可撤销对象和设置。
BEGIN;
CREATE SCHEMA incremental_sort_demo;
SET LOCAL search_path = incremental_sort_demo, public;
SET LOCAL optimizer = off;
SET LOCAL matrix.enable_mxvector = on;
SET LOCAL matrix.enable_mxv_sort = on;
SET LOCAL statement_timeout = '60s';
CREATE TABLE events(id int, a int, b int) USING mars3
WITH (compress_threshold=65535, mars3options='prefer_load_mode=normal')
DISTRIBUTED BY(id);
INSERT INTO events
SELECT g, (g-1)/100, 12001-g FROM generate_series(1,12000) g;
CREATE INDEX events_a ON events USING mars3_btree(a);
ANALYZE events;
a 每 100 行取一个新值,共 120 个取值;b 随 id 增大而减小。索引提供 a 的顺序,而查询要求 ORDER BY a, b, id,因此 a 可以作为前缀。
mars3.enable_btree_inorderscan 控制 mars3_btree 是否提供有序索引路径,默认关闭;不开启时,索引扫描不会带上排序信息,增量排序也就无从利用。为便于观察,示例中临时压低其他路径的竞争力。这些设置不属于日常配置:
SET LOCAL enable_seqscan = off;
SET LOCAL enable_bitmapscan = off;
SET LOCAL enable_sort = off;
SET LOCAL mars3.enable_btree_inorderscan = on;
SET LOCAL enable_incremental_sort = on;
SET LOCAL matrix.enable_mxv_subkey_sort = on;
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT id, a, b FROM events ORDER BY a, b, id LIMIT 50;
Custom Scan (MxVLimit)
-> Custom Scan (MxVMotion) Gather Motion 3:1 (slice1; segments: 3)
Merge Key: a, b, id
-> Custom Scan (MxVLimit)
-> Custom Scan (MxVSort)
Sort Key: a, b, id
Common Prefix Order Key: a
Sort Method: subkey sort
-> Custom Scan (MxVIndexScan) using events_a on events
Optimizer: Postgres-based planner
Common Prefix Order Key 和 Sort Method: subkey sort 是向量增量排序的标志。
SET LOCAL enable_incremental_sort = on;
SET LOCAL matrix.enable_mxv_subkey_sort = off;
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT id, a, b FROM events ORDER BY a, b, id LIMIT 50;
Limit
-> Gather Motion 3:1 (slice1; segments: 3)
Merge Key: a, b, id
-> Limit
-> Incremental Sort
Sort Key: a, b, id
Presorted Key: a
-> Custom Scan (MxVIndexScan) using events_a on events
Optimizer: Postgres-based planner
排序节点保留为原生 Incremental Sort,输入仍然是向量化的索引扫描。关闭的只是向量化增量排序实现,不是向量扫描。
SET LOCAL enable_incremental_sort = off;
SET LOCAL matrix.enable_mxv_subkey_sort = off;
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT id, a, b FROM events ORDER BY a, b, id LIMIT 50;
Custom Scan (MxVLimit)
-> Custom Scan (MxVMotion) Gather Motion 3:1 (slice1; segments: 3)
Merge Key: a, b, id
-> Custom Scan (MxVLimit)
-> Custom Scan (MxVSort)
Sort Key: a, b, id
Sort Method: top-k sort
-> Custom Scan (MxVIndexScan) using events_a on events
Optimizer: Postgres-based planner
MxVSort 仍会出现,但 Sort Method 为 top-k sort 或 quicksort,没有 Common Prefix Order Key,说明走的还是普通排序。扫描用了索引,不代表上层排序利用了索引顺序。
示例结束后清理:
ROLLBACK;
在 EXPLAIN 输出中按以下特征区分:
| 实际实现 | 关键标志 |
|---|---|
| 原生增量排序 | Incremental Sort 节点,带 Presorted Key |
| 向量增量排序 | MxVSort 节点,带 Common Prefix Order Key,且 Sort Method: subkey sort |
| 普通向量排序 | MxVSort 节点,Sort Method 为 top-k sort 或 quicksort |
只看节点名称无法判断:MxVSort 在普通排序和向量增量排序下都会出现。
以上示例输出取自测试环境,行数会随数据分布、统计信息、成本估算和版本变化。上表是在 3 个 Segment 上分布约 12000 行,单个 Segment 的扫描行数在 4000 上下浮动属于正常现象,不要用示例里的行数推算性能倍数。
MxVSort 不会从 MxVIndexScan 直接获得索引提供的排序前缀。若要利用索引顺序,需要 Planner 生成 Incremental Sort 节点并把前缀传给向量排序。因此仅打开 SubKeySorter 而不生成增量排序计划,不会让 MxVIndexScan → MxVSort 这条路径用上索引前缀。enable_incremental_sort=off 不等于关闭全部向量增量排序。 当下层是能够报告顺序的 MxVSort 时,上层普通 MxVSort 仍可能识别该前缀并使用 SubKeySorter。Merge Key 的 Gather Motion 合并。Segment 节点显示的 actual rows 是单个 Segment 的量,不是全表合计。LIMIT 不是必要条件。 它有机会让扫描提前结束,但实际读取量还受前缀组大小、过滤条件、向量批次和分布式执行影响,不保证每次都少读。Incremental Sort。EXPLAIN 中是否为 Optimizer: Postgres-based planner。ANALYZE),成本估算是否倾向了其他路径。比较两种实现时,请在同一查询上分别使用"向量增量排序"与"原生增量排序"两组配置,确认 Sort Method 之后再对比耗时。把"关闭增量排序"与"开启增量排序"相比,得到的是"普通排序 vs 增量排序"的差异,不是两种增量排序实现的对比。
EXPLAIN 显示 Optimizer: Postgres-based plannerPresorted Key 或 Common Prefix Order KeySort Method 为 subkey sortNULLS 位置与所用索引或排序键定义一致ANALYZE,统计信息不是陈旧值