From de645bbe1c27a5ee117fae731da842d8990f87fc Mon Sep 17 00:00:00 2001 From: niebayes Date: Sun, 19 Jul 2026 17:46:27 +0000 Subject: [PATCH 1/5] update docs --- dir.yaml | 3 + .../query-optimization/explain-metrics.md | 102 +++++++++++++++++ .../query-optimization/explain-metrics.md | 103 ++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 en_US/development-guide/query-optimization/explain-metrics.md create mode 100644 zh_CN/development-guide/query-optimization/explain-metrics.md diff --git a/dir.yaml b/dir.yaml index c7a1131..e6e63ec 100644 --- a/dir.yaml +++ b/dir.yaml @@ -109,6 +109,9 @@ - title_en: Last Cache title_cn: Last Cache path: development-guide/query-optimization/last-cache-optimization + - title_en: 查询性能指标说明 + title_cn: Performance Metrics + path: development-guide/query-optimization/explain-metrics - title_en: SQL Hints title_cn: SQL Hints path: development-guide/query-optimization/hints/overview diff --git a/en_US/development-guide/query-optimization/explain-metrics.md b/en_US/development-guide/query-optimization/explain-metrics.md new file mode 100644 index 0000000..68dc0f4 --- /dev/null +++ b/en_US/development-guide/query-optimization/explain-metrics.md @@ -0,0 +1,102 @@ +--- +title: "EXPLAIN ANALYZE Metrics Reference" +description: "A detailed reference for all metrics in Datalayers EXPLAIN ANALYZE output, including names, meanings, and typical values." +--- +# EXPLAIN ANALYZE Metrics Reference + +`EXPLAIN ANALYZE` executes a query while collecting and displaying runtime metrics for each operator in the query plan. This document covers all metrics from `PartitionedScanExec`, `PartitionScanExec`, and their child operators, including both DataFusion native metrics and Datalayers extended metrics. + +--- + +## 1. Frontend Metrics (PartitionedScanExec) + +Frontend-level metrics reflect the query distribution phase on the coordinator node. + +| Metric | Description | Typical Values | +|---|---|---| +| `partition_count` | Number of data partitions involved in the scan | Depends on table partition count, typically 1~N. Larger values indicate broader coverage | +| `elapsed_plan_transform` | Total time for frontend logical analysis, logical optimization, physical planning, and physical optimization | Milliseconds. Simple queries <10ms, complex queries up to tens of ms | +| `elapsed_find_pipelines` | Time spent finding execution pipelines, i.e., matching query constraints to backend pipelines | Microseconds to milliseconds. Usually much smaller than `elapsed_plan_transform` | +| `elapsed_prune_partitions` | Time spent pruning irrelevant partitions using query predicates (time range, partition keys) | Microseconds to milliseconds. Grows with partition count | +| `elapsed_pull_partitions` | Total time spent pulling result streams from all partitions, reflecting distributed query network latency and transfer overhead | Milliseconds to hundreds of ms. Affected by data volume, partition count, and network latency | + +--- + +## 2. Backend Metrics (PartitionScanExec) + +Backend-level metrics reveal the internal query processing details within a single data partition. + +| Metric | Description | Typical Values | +|---|---|---| +| `memtable_count` | Number of memtable sources in `pruned/total` format. `pruned` is the count skipped via precomputed aggregation, `total` is the count after time-range filtering | Typically `total` 0~2. `pruned` may equal `total` for count(*) queries | +| `sst_file_count` | Number of SST (Parquet) file sources in `pruned/total` format. `pruned` is the count skipped via precomputation | `total` depends on file count and query time range. `pruned` can be large for precomputable aggregation queries | +| `range_count` | Number of non-overlapping source groups. In overwrite mode, time-overlapping sources are merged into one range; in append mode, each source forms its own range | Usually ≤ total source count, 1~100+ | +| `elapsed_query_handling` | Total query handling time on this partition, including plan decode, partition scan, and source scanning | Milliseconds to seconds. Depends on data volume and query complexity | +| `elapsed_partition_scan` | Time spent calling `PartitionProvider::scan` to create the `PartitionScanExec` operator, including source listing and scan context construction | Milliseconds, typically < 50ms | +| `elapsed_plan_decode` | Time spent deserializing the Substrait logical plan into an executable plan | Microseconds to milliseconds, typically small | +| `elapsed_plan_transform` | Time spent on backend physical optimization | Microseconds to milliseconds, typically small | +| `elapsed_scan_memtables` | Actual time scanning memtable data (Mutable + Immutable) | Microseconds to milliseconds. Depends on row count in memtables | +| `elapsed_scan_sst_files` | Time scanning SST (Parquet) files, including file listing, metadata reading, and data reading | Milliseconds to seconds. Depends on file count and data volume. Usually the dominant component | + +--- + +## 3. DataFusion Native Metrics (BaselineMetrics) + +These are standard metrics provided by the DataFusion framework for all physical operators, visible on both `PartitionedScanExec` and `PartitionScanExec`. + +| Metric | Description | Typical Values | +|---|---|---| +| `elapsed_compute` | Total wall-clock time spent in computation, including child stream polling time. The timer starts at `execute()` entry and stops when the stream is exhausted | Milliseconds to seconds. The primary reference for perceived operator latency | +| `output_rows` | Total number of rows output by this operator | Depends on predicate selectivity and data volume, 0 to millions+ | +| `mem_used` | Total memory consumed by output RecordBatches (in bytes) | Depends on output row count and column width, KB~GB | + +--- + +## 4. File Scan Metrics (Parquet Level) + +These metrics are natively provided by DataFusion's `ParquetExec` (`DataSourceExec`) and aggregated by Datalayers via `FileScanMetrics`. They appear in the `PartitionScanExec` output only when the query actually scans SST files. + +Pruning metrics (`files_ranges_pruned`, `row_groups_pruned`, `row_groups_pruned_bloom_filter`, `limit_pruned_row_groups`, `page_index_pages_pruned`, `page_index_rows_pruned`) are shown in `pruned/total` format, where `pruned` is the number pruned and `total = pruned + matched` is the total number of containers examined at that pruning tier. + +| Metric | Description | Typical Values | +|---|---|---| +| `files_ranges_pruned` | Number of file ranges pruned or matched by file-level statistics (partition values, file Min/Max, etc.). Pruning often happens at planning time, but may also occur at execution time when dynamic filters (e.g., runtime filters from a Join) provide additional pruning | `pruned/total`. For a single SST scan, `total` is always 1 with `pruned` 0 (scanned) or 1 (fully pruned). Multi-file aggregation may show `pruned > 0` | +| `row_groups_pruned` | Row groups pruned by row-group-level column Min/Max and Null Count statistics | `pruned/total`. `total` equals the total number of row groups in files. Higher `pruned` means better statistics-based filtering | +| `row_groups_pruned_bloom_filter` | Row groups pruned by Bloom Filter | `pruned/total`. Only effective when the Parquet file contains a Bloom Filter and the query predicate hits the Bloom Filter column | +| `limit_pruned_row_groups` | Row groups pruned by LIMIT pushdown (reading stopped early once required rows are satisfied) | `pruned/total`. Only appears in queries with LIMIT. Higher `pruned` means earlier termination | +| `page_index_pages_pruned` | Pages pruned by Page Index level statistics | `pruned/total`. Only effective when files contain Page Index. More `pruned` pages means more I/O saved | +| `page_index_rows_pruned` | Rows pruned by Page Index level statistics | `pruned/total`. Only effective when files contain Page Index. Higher `pruned` means better page-level filtering | +| `bytes_scanned` | Total bytes physically read from the object store | KB~GB, depends on query filters and file size | +| `pushdown_rows_pruned` | Rows filtered out by row-level predicate pushdown (Row Filter during Parquet decoding) | Depends on predicate selectivity and data distribution | +| `pushdown_rows_matched` | Rows that passed the row-level predicate pushdown | `pushdown_rows_matched + pushdown_rows_pruned` = total rows evaluated by predicate pushdown | +| `row_pushdown_eval_time` | Time spent evaluating row-level pushdown filters (nanoseconds, displayed as ms) | Milliseconds, affected by row count | +| `statistics_eval_time` | Time spent evaluating row-group-level statistics filters (nanoseconds, displayed as ms) | Milliseconds, typically much less than I/O time | +| `bloom_filter_eval_time` | Time spent evaluating Bloom Filter (nanoseconds, displayed as ms) | Milliseconds, only present when Bloom Filter is used | +| `page_index_eval_time` | Time spent evaluating Page Index filters (nanoseconds, displayed as ms) | Milliseconds, only present when Page Index is used | +| `metadata_load_time` | Time spent reading and parsing Parquet file footer metadata (nanoseconds, displayed as ms) | Milliseconds, typically < 10ms per file | +| `scan_efficiency` | Scan efficiency percentage = `bytes_scanned / total_bytes * 100%`. `total_bytes` is the uncompressed file size, `bytes_scanned` is the actual bytes read | 0%~100%. Higher means more "wasteful" scanning (more unnecessary data read). Lower means better column pruning and predicate pushdown | + +--- + +## 5. Output Example + +After running `EXPLAIN ANALYZE `, you can find the operator's metrics in the output. Here is an example for `PartitionScanExec`: + +```text +PartitionScanExec: filters=[ts >= 1000 AND ts < 2000], limit=None, output_ordering=[], output_partitions=2, + file_scan=[files_ranges_pruned=0/3, row_groups_pruned=5/12, limit_pruned_row_groups=0/0, + page_index_rows_pruned=0/0, page_index_pages_pruned=0/0, + bytes_scanned=2048000, pushdown_rows_pruned=0, pushdown_rows_matched=100000, + row_pushdown_eval_time=12ms, statistics_eval_time=3ms, page_index_eval_time=0ms, metadata_load_time=5ms, + scan_efficiency=25.0%], + hybrid_cache=off, precomputed=[count(*)=42] + + metrics=[output_rows=100000, elapsed_compute=523ms, mem_used=16384000, memtable_count=0/1, + sst_file_count=1/3, range_count=3, elapsed_query_handling=510ms, elapsed_partition_scan=2ms, + elapsed_plan_decode=1ms, elapsed_plan_transform=0ms, elapsed_scan_memtables=2ms, + elapsed_scan_sst_files=480ms] +``` + +Explanation of `hybrid_cache` and `precomputed` fields: +- **`hybrid_cache`**: Hybrid cache state (`on` / `off`), indicating whether hot data caching is enabled for this partition. +- **`precomputed`**: Precomputed aggregate value. When the query type supports precomputation and source statistics are accurate, this value is returned directly without actual scanning. `none` means no precomputed hit; otherwise, it shows the precomputed aggregation result. diff --git a/zh_CN/development-guide/query-optimization/explain-metrics.md b/zh_CN/development-guide/query-optimization/explain-metrics.md new file mode 100644 index 0000000..5e8e8f6 --- /dev/null +++ b/zh_CN/development-guide/query-optimization/explain-metrics.md @@ -0,0 +1,103 @@ +--- +title: "查询性能指标说明" +description: "说明 Datalayers EXPLAIN ANALYZE 输出中各类 metrics 的名称、含义与典型值,解释查询性能指标。" +--- + +# 查询性能指标 + +Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收集并展示查询计划中每个算子的运行时指标。在这些算子中,`PartitionedScanExec` 和 `PartitionScanExec` 是最重要的两个算子,它们分别对应查询计划的前端和后端阶段。本文档将详细说明这两个算子的指标。 + +--- + +## 1. 前端性能指标(PartitionedScanExec) + +Frontend 层面的指标反映集群协调节点在查询分发阶段的耗时与状态。 + +| Metric 名称 | 含义 | 典型值 | +|---|---|---| +| `partition_count` | 参与本次查询扫描的数据分区数量 | 取决于表的分区数,通常 1~N,值越大说明查询的分区数越多 | +| `elapsed_plan_transform` | 前端进行逻辑计划分析、逻辑优化、物理计划生成、物理优化的总耗时 | 毫秒级,简单查询 <10ms,复杂多级查询可达几十ms | +| `elapsed_find_pipelines` | 查找执行管道(pipelines)的耗时,即匹配查询约束到后端管道的过程 | 微秒~毫秒级,通常远小于 `elapsed_plan_transform` | +| `elapsed_prune_partitions` | 根据查询谓词(时间范围、分区键)裁剪不相关分区的耗时 | 微秒~毫秒级,分区越多耗时越高 | +| `elapsed_pull_partitions` | 从各分区拉取结果流的总耗时,反映分布式查询的网络等待与传输开销 | 毫秒~百毫秒级,受数据量、分区数、网络延迟影响 | + +--- + +## 2. 后端性能指标(PartitionScanExec) + +Backend 层面的指标反映单个数据分区内部的查询处理细节。 + +| Metric 名称 | 含义 | 典型值 | +|---|---|---| +| `memtable_count` | 内存表(Memtable)源的数量,格式为 `pruned/total`,`pruned` 为被预计算裁剪掉的数量,`total` 为经过时间范围过滤后的总数 | 通常 `total` 为 0~2,`pruned` 在 count(*) 类查询中可与 `total` 相等 | +| `sst_file_count` | SST 文件(Parquet)源的数量,格式为 `pruned/total`,`pruned` 为被预计算裁剪掉的数量 | `total` 取决于文件数量和查询时间范围,`pruned` 在可预计算的聚合查询中可能较大 | +| `range_count` | 非重叠源分组的数量。在覆盖写入模式下,时间重叠的源会被合并为一个 range;在追加写入模式下每个源独立为一个 range | 通常等于或小于源总数,1~100+ | +| `elapsed_query_handling` | 该分区整体查询处理的总耗时,包括计划解码、分区扫描、源扫描的全部时间 | 毫秒~秒级,取决于数据量和查询复杂度 | +| `elapsed_partition_scan` | 调用 `PartitionProvider::scan` 创建 `PartitionScanExec` 算子的耗时,包含列出源、构建扫描上下文等 | 毫秒级,通常在几十ms以内 | +| `elapsed_plan_decode` | 将 Substrait 逻辑计划反序列化为可执行计划的耗时 | 微秒~毫秒级,通常较小 | +| `elapsed_plan_transform` | 后端执行物理优化的耗时 | 微秒~毫秒级,通常较小 | +| `elapsed_scan_memtables` | 扫描内存表(Mutable + Immutable)中数据的实际耗时 | 微秒~毫秒级,受 memtable 内数据行数影响 | +| `elapsed_scan_sst_files` | 扫描 SST 文件(Parquet)的耗时,包括文件列表、元数据读取、数据读取等 | 毫秒~秒级,受文件数量和数据量影响,通常是查询中最耗时的部分 | + +--- + +## 3. DataFusion 原生指标(BaselineMetrics) + +这些是 DataFusion 框架提供的所有物理算子的标准指标,在 `PartitionedScanExec` 和 `PartitionScanExec` 均可见。 + +| Metric 名称 | 含义 | 典型值 | +|---|---|---| +| `elapsed_compute` | 算子执行计算的总墙钟时间,包含子流轮询等待时间。该计时器在算子 `execute()` 入口启动,在流耗尽后停止 | 毫秒~秒级,通常是该算子体感耗时的主要参考 | +| `output_rows` | 该算子输出的总行数 | 取决于查询谓词选择性和数据量,0~百万+ | +| `mem_used` | 该算子输出 RecordBatch 所占用的内存总量(字节) | 取决于输出行数和列宽,KB~GB | + +--- + +## 4. 文件扫描指标(Parquet File Scan Metrics) + +以下指标由 DataFusion 的 `ParquetExec`(`DataSourceExec`)原生提供,Datalayers 通过 `FileScanMetrics` 聚合并展示在 `PartitionScanExec` 的输出中。这些指标仅在查询实际涉及 SST 文件扫描时出现。 + +裁剪类指标(`files_ranges_pruned`、`row_groups_pruned`、`row_groups_pruned_bloom_filter`、`limit_pruned_row_groups`、`page_index_pages_pruned`、`page_index_rows_pruned`)格式为 `pruned/total`,其中 `pruned` 为被裁剪掉的数量,`total = pruned + matched` 为该裁剪层面的容器总数。 + +| Metric 名称 | 含义 | 典型值 | +|---|---|---| +| `files_ranges_pruned` | 通过文件级统计信息(分区值、文件 Min/Max 等)裁剪掉的文件/文件段数量。文件裁剪通常在规划阶段完成,但当存在动态过滤器(如 Join 产生的运行时过滤条件)时也可能在执行阶段裁剪 | `pruned/total`,单次 SST 扫描 `total` 恒为 1,`pruned` 为 0(扫描)或 1(完全裁剪),多文件聚合后 `pruned` 可 > 0 | +| `row_groups_pruned` | 通过 Row Group 级别的列 Min/Max 和 Null Count 统计信息裁剪掉的 Row Group 数量 | `pruned/total`,`total` 等于文件中 Row Group 总数,`pruned` 越多说明统计信息过滤效果越好 | +| `row_groups_pruned_bloom_filter` | 通过 Bloom Filter 裁剪掉的 Row Group 数量 | `pruned/total`,仅在 Parquet 文件包含 Bloom Filter 且查询谓词命中 Bloom Filter 列时有效 | +| `limit_pruned_row_groups` | 由于 LIMIT 下推导致的 Row Group 裁剪(满足所需行数后提前停止读取后续 Row Group) | `pruned/total`,仅在包含 LIMIT 的查询中出现,`pruned` 越大说明提前终止得越早 | +| `page_index_pages_pruned` | 通过 Page Index 级别的统计信息裁剪掉的 Page 数量 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越多 I/O 节省越多 | +| `page_index_rows_pruned` | 通过 Page Index 级别的统计信息裁剪掉的行数 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越大说明页级过滤效果越好 | +| `bytes_scanned` | 实际从对象存储物理读取的字节数 | 取决于查询过滤条件和文件大小,KB~GB | +| `pushdown_rows_pruned` | 被行级谓词下推(Parquet 解码期间的 Row Filter)过滤掉的行数 | 取决于谓词选择性和数据分布 | +| `pushdown_rows_matched` | 通过行级谓词下推的行数 | `pushdown_rows_matched + pushdown_rows_pruned` = 谓词下推评估的总行数 | +| `row_pushdown_eval_time` | 行级谓词下推过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,受影响行数影响 | +| `statistics_eval_time` | Row Group 级别统计信息过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,通常远小于 I/O 时间 | +| `bloom_filter_eval_time` | Bloom Filter 过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,仅在使用了 Bloom Filter 时有值 | +| `page_index_eval_time` | Page Index 级别过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,仅在使用了 Page Index 时有值 | +| `metadata_load_time` | 读取和解析 Parquet 文件 Footer 元数据的耗时(纳秒,显示为 ms) | 毫秒级,单个文件通常 <10ms | +| `scan_efficiency` | 扫描效率百分比 = `bytes_scanned / total_bytes * 100%`。`total_bytes` 为文件未压缩总大小,`bytes_scanned` 为实际读取的字节数 | 0%~100%,越高说明扫描越"浪费"(读取了更多不需要的数据),越低说明列裁剪和谓词下推效果越好 | + +--- + +## 5. 查看示例 + +执行 `EXPLAIN ANALYZE ` 后,可在输出中找到对应算子的 metrics 信息。以 `PartitionScanExec` 为例: + +```text +PartitionScanExec: filters=[ts >= 1000 AND ts < 2000], limit=None, output_ordering=[], output_partitions=2, + file_scan=[files_ranges_pruned=0/3, row_groups_pruned=5/12, limit_pruned_row_groups=0/0, + page_index_rows_pruned=0/0, page_index_pages_pruned=0/0, + bytes_scanned=2048000, pushdown_rows_pruned=0, pushdown_rows_matched=100000, + row_pushdown_eval_time=12ms, statistics_eval_time=3ms, page_index_eval_time=0ms, metadata_load_time=5ms, + scan_efficiency=25.0%], + hybrid_cache=off, precomputed=[count(*)=42] + + metrics=[output_rows=100000, elapsed_compute=523ms, mem_used=16384000, memtable_count=0/1, + sst_file_count=1/3, range_count=3, elapsed_query_handling=510ms, elapsed_partition_scan=2ms, + elapsed_plan_decode=1ms, elapsed_plan_transform=0ms, elapsed_scan_memtables=2ms, + elapsed_scan_sst_files=480ms] +``` + +关于 `hybrid_cache` 和 `precomputed` 字段的含义: +- **`hybrid_cache`**:混合缓存开关状态(`on` / `off`),表示该分区是否启用了热点数据缓存。 +- **`precomputed`**:预计算聚合值,当查询类型支持且源统计信息精确可信时,可直接返回预计算值而非实际扫描数据。值为 `none` 表示未命中预计算,否则显示预计算的聚合结果。 From 89f440b9a2f14e22816034872b7c95c9e88294d5 Mon Sep 17 00:00:00 2001 From: niebayes Date: Sun, 19 Jul 2026 18:22:56 +0000 Subject: [PATCH 2/5] minor --- .../query-optimization/explain-metrics.md | 67 +++++++------------ 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/zh_CN/development-guide/query-optimization/explain-metrics.md b/zh_CN/development-guide/query-optimization/explain-metrics.md index 5e8e8f6..7bc9d75 100644 --- a/zh_CN/development-guide/query-optimization/explain-metrics.md +++ b/zh_CN/development-guide/query-optimization/explain-metrics.md @@ -5,65 +5,62 @@ description: "说明 Datalayers EXPLAIN ANALYZE 输出中各类 metrics 的名 # 查询性能指标 -Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收集并展示查询计划中每个算子的运行时指标。在这些算子中,`PartitionedScanExec` 和 `PartitionScanExec` 是最重要的两个算子,它们分别对应查询计划的前端和后端阶段。本文档将详细说明这两个算子的指标。 +Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收集并展示查询计划中每个算子的运行时指标。在这些算子中,`PartitionedScanExec` 和 `PartitionScanExec` 是最重要的两个算子,它们分别对应查询计划的前端和后端阶段。本文档将详细说明这两个算子的指标,同时说明基础指标和文件扫描指标的含义与典型值。 --- ## 1. 前端性能指标(PartitionedScanExec) -Frontend 层面的指标反映集群协调节点在查询分发阶段的耗时与状态。 +PartitionedScanExec 算子的指标反映查询前端节点(负责汇总每个 partition 的查询结果)的性能指标。 | Metric 名称 | 含义 | 典型值 | |---|---|---| | `partition_count` | 参与本次查询扫描的数据分区数量 | 取决于表的分区数,通常 1~N,值越大说明查询的分区数越多 | | `elapsed_plan_transform` | 前端进行逻辑计划分析、逻辑优化、物理计划生成、物理优化的总耗时 | 毫秒级,简单查询 <10ms,复杂多级查询可达几十ms | -| `elapsed_find_pipelines` | 查找执行管道(pipelines)的耗时,即匹配查询约束到后端管道的过程 | 微秒~毫秒级,通常远小于 `elapsed_plan_transform` | +| `elapsed_find_pipelines` | 拆分出可分布式执行的计划的耗时 | 微秒~毫秒级 | | `elapsed_prune_partitions` | 根据查询谓词(时间范围、分区键)裁剪不相关分区的耗时 | 微秒~毫秒级,分区越多耗时越高 | -| `elapsed_pull_partitions` | 从各分区拉取结果流的总耗时,反映分布式查询的网络等待与传输开销 | 毫秒~百毫秒级,受数据量、分区数、网络延迟影响 | +| `elapsed_pull_partitions` | 从各分区拉取结果流的总耗时,反映分布式查询的传输开销,对于集群版则包含额外的网络传输延迟和数据编解码开销 | 毫秒~百毫秒级,受数据量、分区数、网络延迟影响 | --- ## 2. 后端性能指标(PartitionScanExec) -Backend 层面的指标反映单个数据分区内部的查询处理细节。 +PartitionScanExec 算子的指标反映查询后端节点(负责扫描单个数据分区的源数据)的性能指标。后端指标主要关注单个分区内部的查询处理细节,包括内存表扫描、SST 文件扫描、裁剪效率等。 | Metric 名称 | 含义 | 典型值 | |---|---|---| -| `memtable_count` | 内存表(Memtable)源的数量,格式为 `pruned/total`,`pruned` 为被预计算裁剪掉的数量,`total` 为经过时间范围过滤后的总数 | 通常 `total` 为 0~2,`pruned` 在 count(*) 类查询中可与 `total` 相等 | -| `sst_file_count` | SST 文件(Parquet)源的数量,格式为 `pruned/total`,`pruned` 为被预计算裁剪掉的数量 | `total` 取决于文件数量和查询时间范围,`pruned` 在可预计算的聚合查询中可能较大 | -| `range_count` | 非重叠源分组的数量。在覆盖写入模式下,时间重叠的源会被合并为一个 range;在追加写入模式下每个源独立为一个 range | 通常等于或小于源总数,1~100+ | +| `memtable_count` | 扫描的 Memtable 的数量,格式为 `pruned/total`,`pruned` 为被裁剪了的数量,`total` 为经过简单时间范围过滤后的数量 | 例如 2/10 表示 10 个 Memtable 位于查询时间范围内,其中 2 个被裁剪掉 | +| `sst_file_count` | 扫描的 SST 文件的数量,格式为 `pruned/total`,`pruned` 为被裁剪了的数量,`total` 为经过简单时间范围过滤后的数量 | 例如 2/10 表示 10 个 SST 文件位于查询时间范围内,其中 2 个被裁剪掉 | +| `range_count` | 扫描的数据分组的数量。在 overwrite 写入模式下,时间重叠的数据源(Memtable 或 SST)会被合并为一个 range;在追加写入模式下每个源独立为一个 range | 通常等于或小于源总数 | | `elapsed_query_handling` | 该分区整体查询处理的总耗时,包括计划解码、分区扫描、源扫描的全部时间 | 毫秒~秒级,取决于数据量和查询复杂度 | -| `elapsed_partition_scan` | 调用 `PartitionProvider::scan` 创建 `PartitionScanExec` 算子的耗时,包含列出源、构建扫描上下文等 | 毫秒级,通常在几十ms以内 | -| `elapsed_plan_decode` | 将 Substrait 逻辑计划反序列化为可执行计划的耗时 | 微秒~毫秒级,通常较小 | +| `elapsed_partition_scan` | 创建 `PartitionScanExec` 算子的耗时,包含列出数据源、构建扫描上下文等 | 毫秒级,通常在几十ms以内 | +| `elapsed_plan_decode` | 对前端通过网络发送过来的逻辑计划执行反序列化的耗时 | 微秒~毫秒级,通常较小。只对于集群版有意义 | | `elapsed_plan_transform` | 后端执行物理优化的耗时 | 微秒~毫秒级,通常较小 | -| `elapsed_scan_memtables` | 扫描内存表(Mutable + Immutable)中数据的实际耗时 | 微秒~毫秒级,受 memtable 内数据行数影响 | -| `elapsed_scan_sst_files` | 扫描 SST 文件(Parquet)的耗时,包括文件列表、元数据读取、数据读取等 | 毫秒~秒级,受文件数量和数据量影响,通常是查询中最耗时的部分 | +| `elapsed_scan_memtables` | 扫描 Memtable 中数据的实际耗时 | 微秒~毫秒级,受 Memtable 内数据行数影响 | +| `elapsed_scan_sst_files` | 扫描 SST 文件的耗时,包括文件列表、元数据读取、数据读取等 | 毫秒~秒级,受文件数量和数据量影响,通常是查询中最耗时的部分 | --- -## 3. DataFusion 原生指标(BaselineMetrics) +## 3. 基础指标 -这些是 DataFusion 框架提供的所有物理算子的标准指标,在 `PartitionedScanExec` 和 `PartitionScanExec` 均可见。 +我们给几乎所有算子提供了一系列基础指标,用于衡量算子执行的计算耗时、输出行数和内存使用情况。 | Metric 名称 | 含义 | 典型值 | |---|---|---| -| `elapsed_compute` | 算子执行计算的总墙钟时间,包含子流轮询等待时间。该计时器在算子 `execute()` 入口启动,在流耗尽后停止 | 毫秒~秒级,通常是该算子体感耗时的主要参考 | +| `elapsed_compute` | 算子执行计算的总时间,是该算子所有协程计算时间的总和 | 毫秒~秒级,通常是该算子体感耗时的主要参考 | | `output_rows` | 该算子输出的总行数 | 取决于查询谓词选择性和数据量,0~百万+ | | `mem_used` | 该算子输出 RecordBatch 所占用的内存总量(字节) | 取决于输出行数和列宽,KB~GB | --- -## 4. 文件扫描指标(Parquet File Scan Metrics) +## 4. 文件扫描指标 -以下指标由 DataFusion 的 `ParquetExec`(`DataSourceExec`)原生提供,Datalayers 通过 `FileScanMetrics` 聚合并展示在 `PartitionScanExec` 的输出中。这些指标仅在查询实际涉及 SST 文件扫描时出现。 - -裁剪类指标(`files_ranges_pruned`、`row_groups_pruned`、`row_groups_pruned_bloom_filter`、`limit_pruned_row_groups`、`page_index_pages_pruned`、`page_index_rows_pruned`)格式为 `pruned/total`,其中 `pruned` 为被裁剪掉的数量,`total = pruned + matched` 为该裁剪层面的容器总数。 +这些指标仅在查询实际涉及 SST 文件扫描时出现。 | Metric 名称 | 含义 | 典型值 | |---|---|---| | `files_ranges_pruned` | 通过文件级统计信息(分区值、文件 Min/Max 等)裁剪掉的文件/文件段数量。文件裁剪通常在规划阶段完成,但当存在动态过滤器(如 Join 产生的运行时过滤条件)时也可能在执行阶段裁剪 | `pruned/total`,单次 SST 扫描 `total` 恒为 1,`pruned` 为 0(扫描)或 1(完全裁剪),多文件聚合后 `pruned` 可 > 0 | | `row_groups_pruned` | 通过 Row Group 级别的列 Min/Max 和 Null Count 统计信息裁剪掉的 Row Group 数量 | `pruned/total`,`total` 等于文件中 Row Group 总数,`pruned` 越多说明统计信息过滤效果越好 | -| `row_groups_pruned_bloom_filter` | 通过 Bloom Filter 裁剪掉的 Row Group 数量 | `pruned/total`,仅在 Parquet 文件包含 Bloom Filter 且查询谓词命中 Bloom Filter 列时有效 | | `limit_pruned_row_groups` | 由于 LIMIT 下推导致的 Row Group 裁剪(满足所需行数后提前停止读取后续 Row Group) | `pruned/total`,仅在包含 LIMIT 的查询中出现,`pruned` 越大说明提前终止得越早 | | `page_index_pages_pruned` | 通过 Page Index 级别的统计信息裁剪掉的 Page 数量 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越多 I/O 节省越多 | | `page_index_rows_pruned` | 通过 Page Index 级别的统计信息裁剪掉的行数 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越大说明页级过滤效果越好 | @@ -72,32 +69,18 @@ Backend 层面的指标反映单个数据分区内部的查询处理细节。 | `pushdown_rows_matched` | 通过行级谓词下推的行数 | `pushdown_rows_matched + pushdown_rows_pruned` = 谓词下推评估的总行数 | | `row_pushdown_eval_time` | 行级谓词下推过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,受影响行数影响 | | `statistics_eval_time` | Row Group 级别统计信息过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,通常远小于 I/O 时间 | -| `bloom_filter_eval_time` | Bloom Filter 过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,仅在使用了 Bloom Filter 时有值 | | `page_index_eval_time` | Page Index 级别过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,仅在使用了 Page Index 时有值 | | `metadata_load_time` | 读取和解析 Parquet 文件 Footer 元数据的耗时(纳秒,显示为 ms) | 毫秒级,单个文件通常 <10ms | | `scan_efficiency` | 扫描效率百分比 = `bytes_scanned / total_bytes * 100%`。`total_bytes` 为文件未压缩总大小,`bytes_scanned` 为实际读取的字节数 | 0%~100%,越高说明扫描越"浪费"(读取了更多不需要的数据),越低说明列裁剪和谓词下推效果越好 | ---- - -## 5. 查看示例 - -执行 `EXPLAIN ANALYZE ` 后,可在输出中找到对应算子的 metrics 信息。以 `PartitionScanExec` 为例: +### 说明 -```text -PartitionScanExec: filters=[ts >= 1000 AND ts < 2000], limit=None, output_ordering=[], output_partitions=2, - file_scan=[files_ranges_pruned=0/3, row_groups_pruned=5/12, limit_pruned_row_groups=0/0, - page_index_rows_pruned=0/0, page_index_pages_pruned=0/0, - bytes_scanned=2048000, pushdown_rows_pruned=0, pushdown_rows_matched=100000, - row_pushdown_eval_time=12ms, statistics_eval_time=3ms, page_index_eval_time=0ms, metadata_load_time=5ms, - scan_efficiency=25.0%], - hybrid_cache=off, precomputed=[count(*)=42] +文件扫描指标包含一系列裁剪类指标: - metrics=[output_rows=100000, elapsed_compute=523ms, mem_used=16384000, memtable_count=0/1, - sst_file_count=1/3, range_count=3, elapsed_query_handling=510ms, elapsed_partition_scan=2ms, - elapsed_plan_decode=1ms, elapsed_plan_transform=0ms, elapsed_scan_memtables=2ms, - elapsed_scan_sst_files=480ms] -``` +- `files_ranges_pruned` +- `row_groups_pruned` +- `limit_pruned_row_groups` +- `page_index_pages_pruned` +- `page_index_rows_pruned` -关于 `hybrid_cache` 和 `precomputed` 字段的含义: -- **`hybrid_cache`**:混合缓存开关状态(`on` / `off`),表示该分区是否启用了热点数据缓存。 -- **`precomputed`**:预计算聚合值,当查询类型支持且源统计信息精确可信时,可直接返回预计算值而非实际扫描数据。值为 `none` 表示未命中预计算,否则显示预计算的聚合结果。 +它们的格式均为 `pruned/total`,其中 `pruned` 为被裁剪掉的数量,`total = pruned + matched` 为总数。 From 4ac579378a694d41d169bbb92e88d0b15e39bf1b Mon Sep 17 00:00:00 2001 From: YinBo Date: Mon, 20 Jul 2026 15:31:36 +0800 Subject: [PATCH 3/5] chore --- dir.yaml | 3 - .../query-optimization/explain-metrics.md | 102 ------------------ .../query-optimization/explain-metrics.md | 86 --------------- zh_CN/sql-reference/statements/explain.md | 74 +++++++++++++ 4 files changed, 74 insertions(+), 191 deletions(-) delete mode 100644 en_US/development-guide/query-optimization/explain-metrics.md delete mode 100644 zh_CN/development-guide/query-optimization/explain-metrics.md diff --git a/dir.yaml b/dir.yaml index e6e63ec..c7a1131 100644 --- a/dir.yaml +++ b/dir.yaml @@ -109,9 +109,6 @@ - title_en: Last Cache title_cn: Last Cache path: development-guide/query-optimization/last-cache-optimization - - title_en: 查询性能指标说明 - title_cn: Performance Metrics - path: development-guide/query-optimization/explain-metrics - title_en: SQL Hints title_cn: SQL Hints path: development-guide/query-optimization/hints/overview diff --git a/en_US/development-guide/query-optimization/explain-metrics.md b/en_US/development-guide/query-optimization/explain-metrics.md deleted file mode 100644 index 68dc0f4..0000000 --- a/en_US/development-guide/query-optimization/explain-metrics.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: "EXPLAIN ANALYZE Metrics Reference" -description: "A detailed reference for all metrics in Datalayers EXPLAIN ANALYZE output, including names, meanings, and typical values." ---- -# EXPLAIN ANALYZE Metrics Reference - -`EXPLAIN ANALYZE` executes a query while collecting and displaying runtime metrics for each operator in the query plan. This document covers all metrics from `PartitionedScanExec`, `PartitionScanExec`, and their child operators, including both DataFusion native metrics and Datalayers extended metrics. - ---- - -## 1. Frontend Metrics (PartitionedScanExec) - -Frontend-level metrics reflect the query distribution phase on the coordinator node. - -| Metric | Description | Typical Values | -|---|---|---| -| `partition_count` | Number of data partitions involved in the scan | Depends on table partition count, typically 1~N. Larger values indicate broader coverage | -| `elapsed_plan_transform` | Total time for frontend logical analysis, logical optimization, physical planning, and physical optimization | Milliseconds. Simple queries <10ms, complex queries up to tens of ms | -| `elapsed_find_pipelines` | Time spent finding execution pipelines, i.e., matching query constraints to backend pipelines | Microseconds to milliseconds. Usually much smaller than `elapsed_plan_transform` | -| `elapsed_prune_partitions` | Time spent pruning irrelevant partitions using query predicates (time range, partition keys) | Microseconds to milliseconds. Grows with partition count | -| `elapsed_pull_partitions` | Total time spent pulling result streams from all partitions, reflecting distributed query network latency and transfer overhead | Milliseconds to hundreds of ms. Affected by data volume, partition count, and network latency | - ---- - -## 2. Backend Metrics (PartitionScanExec) - -Backend-level metrics reveal the internal query processing details within a single data partition. - -| Metric | Description | Typical Values | -|---|---|---| -| `memtable_count` | Number of memtable sources in `pruned/total` format. `pruned` is the count skipped via precomputed aggregation, `total` is the count after time-range filtering | Typically `total` 0~2. `pruned` may equal `total` for count(*) queries | -| `sst_file_count` | Number of SST (Parquet) file sources in `pruned/total` format. `pruned` is the count skipped via precomputation | `total` depends on file count and query time range. `pruned` can be large for precomputable aggregation queries | -| `range_count` | Number of non-overlapping source groups. In overwrite mode, time-overlapping sources are merged into one range; in append mode, each source forms its own range | Usually ≤ total source count, 1~100+ | -| `elapsed_query_handling` | Total query handling time on this partition, including plan decode, partition scan, and source scanning | Milliseconds to seconds. Depends on data volume and query complexity | -| `elapsed_partition_scan` | Time spent calling `PartitionProvider::scan` to create the `PartitionScanExec` operator, including source listing and scan context construction | Milliseconds, typically < 50ms | -| `elapsed_plan_decode` | Time spent deserializing the Substrait logical plan into an executable plan | Microseconds to milliseconds, typically small | -| `elapsed_plan_transform` | Time spent on backend physical optimization | Microseconds to milliseconds, typically small | -| `elapsed_scan_memtables` | Actual time scanning memtable data (Mutable + Immutable) | Microseconds to milliseconds. Depends on row count in memtables | -| `elapsed_scan_sst_files` | Time scanning SST (Parquet) files, including file listing, metadata reading, and data reading | Milliseconds to seconds. Depends on file count and data volume. Usually the dominant component | - ---- - -## 3. DataFusion Native Metrics (BaselineMetrics) - -These are standard metrics provided by the DataFusion framework for all physical operators, visible on both `PartitionedScanExec` and `PartitionScanExec`. - -| Metric | Description | Typical Values | -|---|---|---| -| `elapsed_compute` | Total wall-clock time spent in computation, including child stream polling time. The timer starts at `execute()` entry and stops when the stream is exhausted | Milliseconds to seconds. The primary reference for perceived operator latency | -| `output_rows` | Total number of rows output by this operator | Depends on predicate selectivity and data volume, 0 to millions+ | -| `mem_used` | Total memory consumed by output RecordBatches (in bytes) | Depends on output row count and column width, KB~GB | - ---- - -## 4. File Scan Metrics (Parquet Level) - -These metrics are natively provided by DataFusion's `ParquetExec` (`DataSourceExec`) and aggregated by Datalayers via `FileScanMetrics`. They appear in the `PartitionScanExec` output only when the query actually scans SST files. - -Pruning metrics (`files_ranges_pruned`, `row_groups_pruned`, `row_groups_pruned_bloom_filter`, `limit_pruned_row_groups`, `page_index_pages_pruned`, `page_index_rows_pruned`) are shown in `pruned/total` format, where `pruned` is the number pruned and `total = pruned + matched` is the total number of containers examined at that pruning tier. - -| Metric | Description | Typical Values | -|---|---|---| -| `files_ranges_pruned` | Number of file ranges pruned or matched by file-level statistics (partition values, file Min/Max, etc.). Pruning often happens at planning time, but may also occur at execution time when dynamic filters (e.g., runtime filters from a Join) provide additional pruning | `pruned/total`. For a single SST scan, `total` is always 1 with `pruned` 0 (scanned) or 1 (fully pruned). Multi-file aggregation may show `pruned > 0` | -| `row_groups_pruned` | Row groups pruned by row-group-level column Min/Max and Null Count statistics | `pruned/total`. `total` equals the total number of row groups in files. Higher `pruned` means better statistics-based filtering | -| `row_groups_pruned_bloom_filter` | Row groups pruned by Bloom Filter | `pruned/total`. Only effective when the Parquet file contains a Bloom Filter and the query predicate hits the Bloom Filter column | -| `limit_pruned_row_groups` | Row groups pruned by LIMIT pushdown (reading stopped early once required rows are satisfied) | `pruned/total`. Only appears in queries with LIMIT. Higher `pruned` means earlier termination | -| `page_index_pages_pruned` | Pages pruned by Page Index level statistics | `pruned/total`. Only effective when files contain Page Index. More `pruned` pages means more I/O saved | -| `page_index_rows_pruned` | Rows pruned by Page Index level statistics | `pruned/total`. Only effective when files contain Page Index. Higher `pruned` means better page-level filtering | -| `bytes_scanned` | Total bytes physically read from the object store | KB~GB, depends on query filters and file size | -| `pushdown_rows_pruned` | Rows filtered out by row-level predicate pushdown (Row Filter during Parquet decoding) | Depends on predicate selectivity and data distribution | -| `pushdown_rows_matched` | Rows that passed the row-level predicate pushdown | `pushdown_rows_matched + pushdown_rows_pruned` = total rows evaluated by predicate pushdown | -| `row_pushdown_eval_time` | Time spent evaluating row-level pushdown filters (nanoseconds, displayed as ms) | Milliseconds, affected by row count | -| `statistics_eval_time` | Time spent evaluating row-group-level statistics filters (nanoseconds, displayed as ms) | Milliseconds, typically much less than I/O time | -| `bloom_filter_eval_time` | Time spent evaluating Bloom Filter (nanoseconds, displayed as ms) | Milliseconds, only present when Bloom Filter is used | -| `page_index_eval_time` | Time spent evaluating Page Index filters (nanoseconds, displayed as ms) | Milliseconds, only present when Page Index is used | -| `metadata_load_time` | Time spent reading and parsing Parquet file footer metadata (nanoseconds, displayed as ms) | Milliseconds, typically < 10ms per file | -| `scan_efficiency` | Scan efficiency percentage = `bytes_scanned / total_bytes * 100%`. `total_bytes` is the uncompressed file size, `bytes_scanned` is the actual bytes read | 0%~100%. Higher means more "wasteful" scanning (more unnecessary data read). Lower means better column pruning and predicate pushdown | - ---- - -## 5. Output Example - -After running `EXPLAIN ANALYZE `, you can find the operator's metrics in the output. Here is an example for `PartitionScanExec`: - -```text -PartitionScanExec: filters=[ts >= 1000 AND ts < 2000], limit=None, output_ordering=[], output_partitions=2, - file_scan=[files_ranges_pruned=0/3, row_groups_pruned=5/12, limit_pruned_row_groups=0/0, - page_index_rows_pruned=0/0, page_index_pages_pruned=0/0, - bytes_scanned=2048000, pushdown_rows_pruned=0, pushdown_rows_matched=100000, - row_pushdown_eval_time=12ms, statistics_eval_time=3ms, page_index_eval_time=0ms, metadata_load_time=5ms, - scan_efficiency=25.0%], - hybrid_cache=off, precomputed=[count(*)=42] - - metrics=[output_rows=100000, elapsed_compute=523ms, mem_used=16384000, memtable_count=0/1, - sst_file_count=1/3, range_count=3, elapsed_query_handling=510ms, elapsed_partition_scan=2ms, - elapsed_plan_decode=1ms, elapsed_plan_transform=0ms, elapsed_scan_memtables=2ms, - elapsed_scan_sst_files=480ms] -``` - -Explanation of `hybrid_cache` and `precomputed` fields: -- **`hybrid_cache`**: Hybrid cache state (`on` / `off`), indicating whether hot data caching is enabled for this partition. -- **`precomputed`**: Precomputed aggregate value. When the query type supports precomputation and source statistics are accurate, this value is returned directly without actual scanning. `none` means no precomputed hit; otherwise, it shows the precomputed aggregation result. diff --git a/zh_CN/development-guide/query-optimization/explain-metrics.md b/zh_CN/development-guide/query-optimization/explain-metrics.md deleted file mode 100644 index 7bc9d75..0000000 --- a/zh_CN/development-guide/query-optimization/explain-metrics.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: "查询性能指标说明" -description: "说明 Datalayers EXPLAIN ANALYZE 输出中各类 metrics 的名称、含义与典型值,解释查询性能指标。" ---- - -# 查询性能指标 - -Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收集并展示查询计划中每个算子的运行时指标。在这些算子中,`PartitionedScanExec` 和 `PartitionScanExec` 是最重要的两个算子,它们分别对应查询计划的前端和后端阶段。本文档将详细说明这两个算子的指标,同时说明基础指标和文件扫描指标的含义与典型值。 - ---- - -## 1. 前端性能指标(PartitionedScanExec) - -PartitionedScanExec 算子的指标反映查询前端节点(负责汇总每个 partition 的查询结果)的性能指标。 - -| Metric 名称 | 含义 | 典型值 | -|---|---|---| -| `partition_count` | 参与本次查询扫描的数据分区数量 | 取决于表的分区数,通常 1~N,值越大说明查询的分区数越多 | -| `elapsed_plan_transform` | 前端进行逻辑计划分析、逻辑优化、物理计划生成、物理优化的总耗时 | 毫秒级,简单查询 <10ms,复杂多级查询可达几十ms | -| `elapsed_find_pipelines` | 拆分出可分布式执行的计划的耗时 | 微秒~毫秒级 | -| `elapsed_prune_partitions` | 根据查询谓词(时间范围、分区键)裁剪不相关分区的耗时 | 微秒~毫秒级,分区越多耗时越高 | -| `elapsed_pull_partitions` | 从各分区拉取结果流的总耗时,反映分布式查询的传输开销,对于集群版则包含额外的网络传输延迟和数据编解码开销 | 毫秒~百毫秒级,受数据量、分区数、网络延迟影响 | - ---- - -## 2. 后端性能指标(PartitionScanExec) - -PartitionScanExec 算子的指标反映查询后端节点(负责扫描单个数据分区的源数据)的性能指标。后端指标主要关注单个分区内部的查询处理细节,包括内存表扫描、SST 文件扫描、裁剪效率等。 - -| Metric 名称 | 含义 | 典型值 | -|---|---|---| -| `memtable_count` | 扫描的 Memtable 的数量,格式为 `pruned/total`,`pruned` 为被裁剪了的数量,`total` 为经过简单时间范围过滤后的数量 | 例如 2/10 表示 10 个 Memtable 位于查询时间范围内,其中 2 个被裁剪掉 | -| `sst_file_count` | 扫描的 SST 文件的数量,格式为 `pruned/total`,`pruned` 为被裁剪了的数量,`total` 为经过简单时间范围过滤后的数量 | 例如 2/10 表示 10 个 SST 文件位于查询时间范围内,其中 2 个被裁剪掉 | -| `range_count` | 扫描的数据分组的数量。在 overwrite 写入模式下,时间重叠的数据源(Memtable 或 SST)会被合并为一个 range;在追加写入模式下每个源独立为一个 range | 通常等于或小于源总数 | -| `elapsed_query_handling` | 该分区整体查询处理的总耗时,包括计划解码、分区扫描、源扫描的全部时间 | 毫秒~秒级,取决于数据量和查询复杂度 | -| `elapsed_partition_scan` | 创建 `PartitionScanExec` 算子的耗时,包含列出数据源、构建扫描上下文等 | 毫秒级,通常在几十ms以内 | -| `elapsed_plan_decode` | 对前端通过网络发送过来的逻辑计划执行反序列化的耗时 | 微秒~毫秒级,通常较小。只对于集群版有意义 | -| `elapsed_plan_transform` | 后端执行物理优化的耗时 | 微秒~毫秒级,通常较小 | -| `elapsed_scan_memtables` | 扫描 Memtable 中数据的实际耗时 | 微秒~毫秒级,受 Memtable 内数据行数影响 | -| `elapsed_scan_sst_files` | 扫描 SST 文件的耗时,包括文件列表、元数据读取、数据读取等 | 毫秒~秒级,受文件数量和数据量影响,通常是查询中最耗时的部分 | - ---- - -## 3. 基础指标 - -我们给几乎所有算子提供了一系列基础指标,用于衡量算子执行的计算耗时、输出行数和内存使用情况。 - -| Metric 名称 | 含义 | 典型值 | -|---|---|---| -| `elapsed_compute` | 算子执行计算的总时间,是该算子所有协程计算时间的总和 | 毫秒~秒级,通常是该算子体感耗时的主要参考 | -| `output_rows` | 该算子输出的总行数 | 取决于查询谓词选择性和数据量,0~百万+ | -| `mem_used` | 该算子输出 RecordBatch 所占用的内存总量(字节) | 取决于输出行数和列宽,KB~GB | - ---- - -## 4. 文件扫描指标 - -这些指标仅在查询实际涉及 SST 文件扫描时出现。 - -| Metric 名称 | 含义 | 典型值 | -|---|---|---| -| `files_ranges_pruned` | 通过文件级统计信息(分区值、文件 Min/Max 等)裁剪掉的文件/文件段数量。文件裁剪通常在规划阶段完成,但当存在动态过滤器(如 Join 产生的运行时过滤条件)时也可能在执行阶段裁剪 | `pruned/total`,单次 SST 扫描 `total` 恒为 1,`pruned` 为 0(扫描)或 1(完全裁剪),多文件聚合后 `pruned` 可 > 0 | -| `row_groups_pruned` | 通过 Row Group 级别的列 Min/Max 和 Null Count 统计信息裁剪掉的 Row Group 数量 | `pruned/total`,`total` 等于文件中 Row Group 总数,`pruned` 越多说明统计信息过滤效果越好 | -| `limit_pruned_row_groups` | 由于 LIMIT 下推导致的 Row Group 裁剪(满足所需行数后提前停止读取后续 Row Group) | `pruned/total`,仅在包含 LIMIT 的查询中出现,`pruned` 越大说明提前终止得越早 | -| `page_index_pages_pruned` | 通过 Page Index 级别的统计信息裁剪掉的 Page 数量 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越多 I/O 节省越多 | -| `page_index_rows_pruned` | 通过 Page Index 级别的统计信息裁剪掉的行数 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越大说明页级过滤效果越好 | -| `bytes_scanned` | 实际从对象存储物理读取的字节数 | 取决于查询过滤条件和文件大小,KB~GB | -| `pushdown_rows_pruned` | 被行级谓词下推(Parquet 解码期间的 Row Filter)过滤掉的行数 | 取决于谓词选择性和数据分布 | -| `pushdown_rows_matched` | 通过行级谓词下推的行数 | `pushdown_rows_matched + pushdown_rows_pruned` = 谓词下推评估的总行数 | -| `row_pushdown_eval_time` | 行级谓词下推过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,受影响行数影响 | -| `statistics_eval_time` | Row Group 级别统计信息过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,通常远小于 I/O 时间 | -| `page_index_eval_time` | Page Index 级别过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,仅在使用了 Page Index 时有值 | -| `metadata_load_time` | 读取和解析 Parquet 文件 Footer 元数据的耗时(纳秒,显示为 ms) | 毫秒级,单个文件通常 <10ms | -| `scan_efficiency` | 扫描效率百分比 = `bytes_scanned / total_bytes * 100%`。`total_bytes` 为文件未压缩总大小,`bytes_scanned` 为实际读取的字节数 | 0%~100%,越高说明扫描越"浪费"(读取了更多不需要的数据),越低说明列裁剪和谓词下推效果越好 | - -### 说明 - -文件扫描指标包含一系列裁剪类指标: - -- `files_ranges_pruned` -- `row_groups_pruned` -- `limit_pruned_row_groups` -- `page_index_pages_pruned` -- `page_index_rows_pruned` - -它们的格式均为 `pruned/total`,其中 `pruned` 为被裁剪掉的数量,`total = pruned + matched` 为总数。 diff --git a/zh_CN/sql-reference/statements/explain.md b/zh_CN/sql-reference/statements/explain.md index 9f81f11..631fe57 100644 --- a/zh_CN/sql-reference/statements/explain.md +++ b/zh_CN/sql-reference/statements/explain.md @@ -23,3 +23,77 @@ ANALYZE: 执行语句并测量每个计划节点花费的时间与获取数据 ```sql EXPLAIN ANALYZE SELECT * FROM products LIMIT 5 OFFSET 5; ``` + +## 指标说明 + +Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收集并展示查询计划中每个算子的运行时指标。在这些算子中,`PartitionedScanExec` 和 `PartitionScanExec` 是最重要的两个算子,它们分别对应查询计划的前端和后端阶段。本文档将详细说明这两个算子的指标,同时说明基础指标和文件扫描指标的含义与典型值。 + +### 前端性能指标(PartitionedScanExec) + +PartitionedScanExec 算子的指标反映查询前端节点(负责汇总每个 partition 的查询结果)的性能指标。 + +| Metric 名称 | 含义 | 典型值 | +| --- | --- | --- | +| `partition_count` | 参与本次查询扫描的数据分区数量 | 取决于表的分区数,通常 1~N,值越大说明查询的分区数越多 | +| `elapsed_plan_transform` | 前端进行逻辑计划分析、逻辑优化、物理计划生成、物理优化的总耗时 | 毫秒级,简单查询 <10ms,复杂多级查询可达几十ms | +| `elapsed_find_pipelines` | 拆分出可分布式执行的计划的耗时 | 微秒~毫秒级 | +| `elapsed_prune_partitions` | 根据查询谓词(时间范围、分区键)裁剪不相关分区的耗时 | 微秒~毫秒级,分区越多耗时越高 | +| `elapsed_pull_partitions` | 从各分区拉取结果流的总耗时,反映分布式查询的传输开销,对于集群版则包含额外的网络传输延迟和数据编解码开销 | 毫秒~百毫秒级,受数据量、分区数、网络延迟影响 | + +### 后端性能指标(PartitionScanExec) + +PartitionScanExec 算子的指标反映查询后端节点(负责扫描单个数据分区的源数据)的性能指标。后端指标主要关注单个分区内部的查询处理细节,包括内存表扫描、SST 文件扫描、裁剪效率等。 + +| Metric 名称 | 含义 | 典型值 | +| --- | --- | --- | +| `memtable_count` | 扫描的 Memtable 的数量,格式为 `pruned/total`,`pruned` 为被裁剪了的数量,`total` 为经过简单时间范围过滤后的数量 | 例如 2/10 表示 10 个 Memtable 位于查询时间范围内,其中 2 个被裁剪掉 | +| `sst_file_count` | 扫描的 SST 文件的数量,格式为 `pruned/total`,`pruned` 为被裁剪了的数量,`total` 为经过简单时间范围过滤后的数量 | 例如 2/10 表示 10 个 SST 文件位于查询时间范围内,其中 2 个被裁剪掉 | +| `range_count` | 扫描的数据分组的数量。在 overwrite 写入模式下,时间重叠的数据源(Memtable 或 SST)会被合并为一个 range;在追加写入模式下每个源独立为一个 range | 通常等于或小于源总数 | +| `elapsed_query_handling` | 该分区整体查询处理的总耗时,包括计划解码、分区扫描、源扫描的全部时间 | 毫秒~秒级,取决于数据量和查询复杂度 | +| `elapsed_partition_scan` | 创建 `PartitionScanExec` 算子的耗时,包含列出数据源、构建扫描上下文等 | 毫秒级,通常在几十ms以内 | +| `elapsed_plan_decode` | 对前端通过网络发送过来的逻辑计划执行反序列化的耗时 | 微秒~毫秒级,通常较小。只对于集群版有意义 | +| `elapsed_plan_transform` | 后端执行物理优化的耗时 | 微秒~毫秒级,通常较小 | +| `elapsed_scan_memtables` | 扫描 Memtable 中数据的实际耗时 | 微秒~毫秒级,受 Memtable 内数据行数影响 | +| `elapsed_scan_sst_files` | 扫描 SST 文件的耗时,包括文件列表、元数据读取、数据读取等 | 毫秒~秒级,受文件数量和数据量影响,通常是查询中最耗时的部分 | + +### 基础指标 + +我们给几乎所有算子提供了一系列基础指标,用于衡量算子执行的计算耗时、输出行数和内存使用情况。 + +| Metric 名称 | 含义 | 典型值 | +| --- | --- | --- | +| `elapsed_compute` | 算子执行计算的总时间,是该算子所有协程计算时间的总和 | 毫秒~秒级,通常是该算子体感耗时的主要参考 | +| `output_rows` | 该算子输出的总行数 | 取决于查询谓词选择性和数据量,0~百万+ | +| `mem_used` | 该算子输出 RecordBatch 所占用的内存总量(字节) | 取决于输出行数和列宽,KB~GB | + +### 文件扫描指标 + +这些指标仅在查询实际涉及 SST 文件扫描时出现。 + +| Metric 名称 | 含义 | 典型值 | +| --- | --- | --- | +| `files_ranges_pruned` | 通过文件级统计信息(分区值、文件 Min/Max 等)裁剪掉的文件/文件段数量。文件裁剪通常在规划阶段完成,但当存在动态过滤器(如 Join 产生的运行时过滤条件)时也可能在执行阶段裁剪 | `pruned/total`,单次 SST 扫描 `total` 恒为 1,`pruned` 为 0(扫描)或 1(完全裁剪),多文件聚合后 `pruned` 可 > 0 | +| `row_groups_pruned` | 通过 Row Group 级别的列 Min/Max 和 Null Count 统计信息裁剪掉的 Row Group 数量 | `pruned/total`,`total` 等于文件中 Row Group 总数,`pruned` 越多说明统计信息过滤效果越好 | +| `limit_pruned_row_groups` | 由于 LIMIT 下推导致的 Row Group 裁剪(满足所需行数后提前停止读取后续 Row Group) | `pruned/total`,仅在包含 LIMIT 的查询中出现,`pruned` 越大说明提前终止得越早 | +| `page_index_pages_pruned` | 通过 Page Index 级别的统计信息裁剪掉的 Page 数量 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越多 I/O 节省越多 | +| `page_index_rows_pruned` | 通过 Page Index 级别的统计信息裁剪掉的行数 | `pruned/total`,仅在文件包含 Page Index 时有效,`pruned` 越大说明页级过滤效果越好 | +| `bytes_scanned` | 实际从对象存储物理读取的字节数 | 取决于查询过滤条件和文件大小,KB~GB | +| `pushdown_rows_pruned` | 被行级谓词下推(Parquet 解码期间的 Row Filter)过滤掉的行数 | 取决于谓词选择性和数据分布 | +| `pushdown_rows_matched` | 通过行级谓词下推的行数 | `pushdown_rows_matched + pushdown_rows_pruned` = 谓词下推评估的总行数 | +| `row_pushdown_eval_time` | 行级谓词下推过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,受影响行数影响 | +| `statistics_eval_time` | Row Group 级别统计信息过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,通常远小于 I/O 时间 | +| `page_index_eval_time` | Page Index 级别过滤的评估耗时(纳秒,显示为 ms) | 毫秒级,仅在使用了 Page Index 时有值 | +| `metadata_load_time` | 读取和解析 Parquet 文件 Footer 元数据的耗时(纳秒,显示为 ms) | 毫秒级,单个文件通常 <10ms | +| `scan_efficiency` | 扫描效率百分比 = `bytes_scanned / total_bytes * 100%`。`total_bytes` 为文件未压缩总大小,`bytes_scanned` 为实际读取的字节数 | 0%~100%,越高说明扫描越"浪费"(读取了更多不需要的数据),越低说明列裁剪和谓词下推效果越好 | + +#### 说明 + +文件扫描指标包含一系列裁剪类指标: + +- `files_ranges_pruned` +- `row_groups_pruned` +- `limit_pruned_row_groups` +- `page_index_pages_pruned` +- `page_index_rows_pruned` + +它们的格式均为 `pruned/total`,其中 `pruned` 为被裁剪掉的数量,`total = pruned + matched` 为总数 From f81b102f22504a6a7d8bbd4767417652a4cc85a2 Mon Sep 17 00:00:00 2001 From: YinBo Date: Mon, 20 Jul 2026 15:37:02 +0800 Subject: [PATCH 4/5] chore --- zh_CN/sql-reference/statements/explain.md | 48 +++++++++++++++++------ 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/zh_CN/sql-reference/statements/explain.md b/zh_CN/sql-reference/statements/explain.md index 631fe57..3bad71b 100644 --- a/zh_CN/sql-reference/statements/explain.md +++ b/zh_CN/sql-reference/statements/explain.md @@ -1,36 +1,55 @@ --- title: "EXPLAIN 语句详解" -description: "EXPLAIN 语句是 Datalayers 提供的强大查询分析工具,用于展示查询语句的执行计划。通过分析执行计划,用户可以深入了解查询的底层执行逻辑,识别性能瓶颈,并进行针对性的优化。" +description: "介绍 Datalayers EXPLAIN 与 EXPLAIN ANALYZE 的语法、使用方式和关键指标,帮助你分析查询执行计划并定位性能瓶颈。" --- # EXPLAIN 语句详解 ## 功能概述 -EXPLAIN 语句是 Datalayers 提供的强大查询分析工具,用于展示查询语句的执行计划。通过分析执行计划,用户可以深入了解查询的底层执行逻辑,识别性能瓶颈,并进行针对性的优化。 +EXPLAIN 语句用于展示查询语句的执行计划,帮助你了解查询的执行路径、数据扫描范围以及可能的性能瓶颈。在需要定位慢查询原因或验证优化效果时,EXPLAIN 是最常用的分析工具之一。 ## 语法 ```sql -EXPLAIN [ANALYZE] [verbose] SELECT +EXPLAIN [ANALYZE] [VERBOSE] SELECT ``` ::: tip -ANALYZE: 执行语句并测量每个计划节点花费的时间与获取数据的记录数等。 +- `EXPLAIN`:只展示执行计划,不执行查询。 +- `EXPLAIN ANALYZE`:会真实执行查询,并收集每个计划节点的运行时指标,如耗时、扫描行数和裁剪效果。 +- `EXPLAIN VERBOSE`:输出更详细的计划信息,适合在排查复杂查询时使用。 ::: ## 示例 ```sql -EXPLAIN ANALYZE SELECT * FROM products LIMIT 5 OFFSET 5; +EXPLAIN ANALYZE +SELECT * +FROM test.device +WHERE sid = 1 + AND ts >= '2024-09-01 00:00:00' + AND ts < '2024-09-02 00:00:00' +ORDER BY ts DESC +LIMIT 10; ``` ## 指标说明 -Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收集并展示查询计划中每个算子的运行时指标。在这些算子中,`PartitionedScanExec` 和 `PartitionScanExec` 是最重要的两个算子,它们分别对应查询计划的前端和后端阶段。本文档将详细说明这两个算子的指标,同时说明基础指标和文件扫描指标的含义与典型值。 +Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收集并展示查询计划中各个算子的运行时指标。在时序表查询场景中,最值得重点关注的通常是 `PartitionedScanExec` 和 `PartitionScanExec` 两个算子,它们分别对应查询计划的前端汇总阶段和后端分区扫描阶段。本文档重点说明这两个算子的指标,并补充基础指标与文件扫描指标的含义和典型值。 + +### 如何阅读 EXPLAIN ANALYZE 输出 + +面对一棵较长的执行计划时,建议按以下顺序阅读: + +1. 先看 `PartitionedScanExec`,确认查询涉及多少分区,以及前端规划、分区裁剪和结果拉取分别花了多少时间。 +2. 再看 `PartitionScanExec`,判断每个分区主要在扫描 Memtable 还是 SST 文件,以及是否发生了有效裁剪。 +3. 如果查询明显慢在存储访问,再继续查看文件扫描指标,例如 `bytes_scanned`、`row_groups_pruned`、`page_index_rows_pruned` 和 `scan_efficiency`,判断 I/O 是否存在浪费。 + +如果只想快速判断查询是否命中了时间范围裁剪、分区裁剪或对象存储优化,通常只需要重点查看上述几类指标即可。 ### 前端性能指标(PartitionedScanExec) -PartitionedScanExec 算子的指标反映查询前端节点(负责汇总每个 partition 的查询结果)的性能指标。 +`PartitionedScanExec` 的指标反映查询前端节点的性能情况。该节点负责汇总各个 partition 的查询结果,并协调分布式执行过程。 | Metric 名称 | 含义 | 典型值 | | --- | --- | --- | @@ -42,7 +61,7 @@ PartitionedScanExec 算子的指标反映查询前端节点(负责汇总每个 ### 后端性能指标(PartitionScanExec) -PartitionScanExec 算子的指标反映查询后端节点(负责扫描单个数据分区的源数据)的性能指标。后端指标主要关注单个分区内部的查询处理细节,包括内存表扫描、SST 文件扫描、裁剪效率等。 +`PartitionScanExec` 的指标反映查询后端节点的性能情况。该节点负责扫描单个数据分区中的源数据,因此这些指标主要用于判断 Memtable 扫描、SST 文件扫描以及各类裁剪是否生效。 | Metric 名称 | 含义 | 典型值 | | --- | --- | --- | @@ -58,7 +77,7 @@ PartitionScanExec 算子的指标反映查询后端节点(负责扫描单个 ### 基础指标 -我们给几乎所有算子提供了一系列基础指标,用于衡量算子执行的计算耗时、输出行数和内存使用情况。 +几乎所有算子都会提供一组基础指标,用于衡量算子执行过程中的计算耗时、输出行数和内存使用情况。 | Metric 名称 | 含义 | 典型值 | | --- | --- | --- | @@ -88,7 +107,7 @@ PartitionScanExec 算子的指标反映查询后端节点(负责扫描单个 #### 说明 -文件扫描指标包含一系列裁剪类指标: +文件扫描指标中包含一系列裁剪类指标: - `files_ranges_pruned` - `row_groups_pruned` @@ -96,4 +115,11 @@ PartitionScanExec 算子的指标反映查询后端节点(负责扫描单个 - `page_index_pages_pruned` - `page_index_rows_pruned` -它们的格式均为 `pruned/total`,其中 `pruned` 为被裁剪掉的数量,`total = pruned + matched` 为总数 +它们的格式均为 `pruned/total`,其中 `pruned` 表示被裁剪掉的数量,`matched` 表示实际参与扫描或命中的数量,因此 `total = pruned + matched`。 + +当这些指标中的 `pruned` 值较高时,通常说明时间范围过滤、统计信息过滤或 Page Index 过滤生效较好;如果 `pruned` 长期接近 0,而 `bytes_scanned` 又较大,则可以进一步检查查询谓词、分区设计和排序方式是否有优化空间。 + +## 相关文档 + +- 想了解整体调优方向,请参考 [查询性能调优概述](../../development-guide/query-performance-tuning-overview.md) +- 想结合缓存策略分析扫描性能,请参考 [Hybrid Cache 优化指南](../../development-guide/query-optimization/hints/hybrid-cache.md) From 75f716bf0d65f47f2bc936b4ec86f8ec5fbf4838 Mon Sep 17 00:00:00 2001 From: YinBo Date: Mon, 20 Jul 2026 15:40:21 +0800 Subject: [PATCH 5/5] chore --- zh_CN/sql-reference/statements/explain.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/zh_CN/sql-reference/statements/explain.md b/zh_CN/sql-reference/statements/explain.md index 3bad71b..aed1d6e 100644 --- a/zh_CN/sql-reference/statements/explain.md +++ b/zh_CN/sql-reference/statements/explain.md @@ -15,6 +15,7 @@ EXPLAIN [ANALYZE] [VERBOSE] SELECT ``` ::: tip + - `EXPLAIN`:只展示执行计划,不执行查询。 - `EXPLAIN ANALYZE`:会真实执行查询,并收集每个计划节点的运行时指标,如耗时、扫描行数和裁剪效果。 - `EXPLAIN VERBOSE`:输出更详细的计划信息,适合在排查复杂查询时使用。 @@ -27,8 +28,8 @@ EXPLAIN ANALYZE SELECT * FROM test.device WHERE sid = 1 - AND ts >= '2024-09-01 00:00:00' - AND ts < '2024-09-02 00:00:00' + AND ts >= '2024-09-01 00:00:00' + AND ts < '2024-09-02 00:00:00' ORDER BY ts DESC LIMIT 10; ``` @@ -117,8 +118,6 @@ Datalayers 提供 `EXPLAIN ANALYZE` 功能,用于在执行查询的同时收 它们的格式均为 `pruned/total`,其中 `pruned` 表示被裁剪掉的数量,`matched` 表示实际参与扫描或命中的数量,因此 `total = pruned + matched`。 -当这些指标中的 `pruned` 值较高时,通常说明时间范围过滤、统计信息过滤或 Page Index 过滤生效较好;如果 `pruned` 长期接近 0,而 `bytes_scanned` 又较大,则可以进一步检查查询谓词、分区设计和排序方式是否有优化空间。 - ## 相关文档 - 想了解整体调优方向,请参考 [查询性能调优概述](../../development-guide/query-performance-tuning-overview.md)