查询优化方案
一个实用的操作手册:症状 → 根本原因 → 经过验证的解决方案。
当你打开一个 profile 并发现一个警示指标,但仍需回答“接下来怎么办?”时使用。
1 · 快速诊断流程
-
浏览执行概览
如果QueryPeakMemoryUsagePerNode > 80 %或QuerySpillBytes > 1 GB,直接跳到内存和溢出方案。 -
找到最慢的 Pipeline / Operator
⟶ 在 Query Profile UI 中点击 Sort by OperatorTotalTime %。
最热的 operator 告诉你接下来要阅读哪个方案块(Scan, Join, Aggregate, …)。 -
确认瓶颈子类型
每个方案以其特征指标模式开始。在尝试解决方案之前匹配这些模式。
2 · 按 Operator 分类的方案
2.1 OLAP / Connector Scan [metrics]
为了更好地理解 Scan Operator 中的各种指标,以下图示展示了这些指标与存储结构之间的关联。

为了从磁盘检索数据并应用谓词,存储引擎使用了几种技术:
- 数据存储:编码和压缩的数据以段的形式存储在磁盘上,并附带各种索引。
- 索引过滤:引擎利用 BitmapIndex、BloomfilterIndex、ZonemapIndex、ShortKeyIndex 和 NGramIndex 等索引来跳过不必要的数据。
- 下推谓词:简单的谓词,如
a > 1,被下推到特定列进行评估。 - 延迟物化:仅从磁盘中检索所需的列和过滤后的行。
- 非下推谓词:无法下推的谓词会被评估。
- 投影表达式:计算诸如
SELECT a + 1的表达式。
Scan Operator 使用一个额外的线程池来执行 IO 任务。因此,该节点的时间指标关系如下图所示:

常见性能瓶颈
冷或慢存储 – 当 BytesRead、ScanTime 或 IOTaskExecTime 占主导地位且磁盘 I/O 徘徊在 80-100% 时,扫描正在命中冷或配置不足的存储。将热数据移动到 NVMe/SSD,启用存储缓存,或者如果你正在扫描 S3/HDFS,提高 remote_cache_capacity。
缺少过滤下推 – 如果 PushdownPredicates 接近 0 而 ExprFilterRows 很高,谓词没有到达存储层。将它们重写为简单比较(避免 %LIKE% 和宽 OR 链)或添加 zonemap/Bloom 索引或物化视图以便下推。
线程池饥饿 – 高 IOTaskWaitTime 以及低 PeakIOTasks 表明 I/O 线程池已饱和。增加 BE 上的 max_io_threads 或扩大缓存以让更多任务并行运行。
tablet 数据倾斜 – 最大和最小 OperatorTotalTime 之间的巨大差距意味着某些 tablets 的工作量比其他的多。重新分桶到更高基数的键或增加桶数以分散负载。
Rowset/segment 碎片化 – 爆炸性的 RowsetsReadCount/SegmentsReadCount 加上长时间的 SegmentInitTime 表示有许多小的 rowsets。触发手动 compaction 并批量小型导入以便段提前合并。
累积的软删除 – 大量的 DeleteFilterRows 表示大量使用软删除。运行 BE compaction 以清除墓碑并合并删除位图。