文档文档

分析查询计划

了解如何读取和分析查询计划,以理解查询执行步骤和数据组织,并找到性能瓶颈。

当您查询 InfluxDB 3 时,查询器会设计一个查询计划来执行查询。引擎会尝试为查询结构和数据确定最佳计划。通过学习如何生成和解释查询计划的报告,您可以更好地理解查询是如何执行的,并识别影响查询性能的瓶颈。

例如,如果查询计划显示您的查询读取了大量 Parquet 文件,您可以采取措施优化您的查询,例如添加过滤器以读取更少的数据,或配置您的集群以存储更少但更大的文件。

使用 EXPLAIN 关键字查看查询计划

使用 EXPLAIN 关键字(以及可选的 ANALYZEVERBOSE 关键字)来查看查询的查询计划。

使用 Python 和 pandas 查看 EXPLAIN 报告

阅读 EXPLAIN 报告

当您使用 EXPLAIN 关键字查看查询计划时,报告包含以下内容

  • 两列:plan_typeplan
  • 逻辑计划logical_plan)的一行
  • 物理计划physical_plan)的一行

阅读查询计划

计划采用树状格式——每个计划都是一个倒置的树,其中执行和数据流从叶节点(计划中最内层的步骤)流向外部分支节点。 无论是阅读逻辑计划还是物理计划,请记住以下几点

  • 叶节点开始,向上阅读。
  • 在计划的顶部,根节点代表最终的、包含所有步骤的步骤。

物理计划中,每个步骤都是一个ExecutionPlan 节点,它接收输入数据和输出需求的表达式,并计算数据分区。

使用以下步骤分析查询计划,并估计完成查询所需的工作量。无论计划看起来多么庞大或复杂,这些步骤都适用。

  1. 从最深缩进的步骤(叶节点)开始,向上阅读。
  2. 理解每个ExecutionPlan 节点的工作——例如,包含叶节点的 UnionExec 节点意味着 UnionExec 连接所有叶节点的输出。
  3. 对于每个表达式,回答以下问题
    • 计划的数据输入的形状和大小是什么?
    • 计划的数据输出的形状和大小是什么?

本指南的其余部分将引导您分析物理计划。 理解查询计划中节点的顺序、角色、输入和输出可以帮助您估计总体工作负载,并找到查询中的潜在瓶颈。

SELECT - ORDER BY 查询的物理计划示例

以下示例展示了如何读取 EXPLAIN 报告和物理查询计划。

给定 h20 测量数据和以下查询

EXPLAIN SELECT city, min_temp, time FROM h2o ORDER BY city ASC, time DESC;

输出类似于以下内容

EXPLAIN 报告

| plan_type     | plan                                                                     |
+---------------+--------------------------------------------------------------------------+
| logical_plan  | Sort: h2o.city ASC NULLS LAST, h2o.time DESC NULLS FIRST                 |
|               |   TableScan: h2o projection=[city, min_temp, time]                       |
| physical_plan | SortPreservingMergeExec: [city@0 ASC NULLS LAST,time@2 DESC]             |
|               |   UnionExec                                                              |
|               |     SortExec: expr=[city@0 ASC NULLS LAST,time@2 DESC]                   |
|               |       ParquetExec: file_groups={...}, projection=[city, min_temp, time]  |
|               |     SortExec: expr=[city@0 ASC NULLS LAST,time@2 DESC]                   |
|               |       ParquetExec: file_groups={...}, projection=[city, min_temp, time]  |
|               |                                                                          |

EXPLAIN SELECT city, min_temp, time FROM h2o ORDER BY city ASC, time DESC; 的输出

物理计划中的每个步骤或节点都是一个 ExecutionPlan 名称和包含查询相关部分的键值表达式——例如,EXPLAIN 报告物理计划中的第一个节点是 ParquetExec 执行计划

ParquetExec: file_groups={...}, projection=[city, min_temp, time]

由于 ParquetExecRecordBatchesExec 节点在 InfluxDB 查询中检索和扫描数据,因此每个查询计划都以一个或多个这些节点开始。

物理计划数据流

数据在查询计划中向上流动。

下图显示了EXPLAIN 报告物理计划中的数据流和节点顺序

SortPreservingMergeExec
UnionExec
SortExec
ParquetExec
SortExec
ParquetExec

EXPLAIN 报告物理计划中的执行和数据流。 ParquetExec 节点并行执行,UnionExec 组合它们的输出。

以下步骤总结了物理计划执行和数据流

  1. 两个 ParquetExec 计划并行从 Parquet 文件中读取数据
    • 每个 ParquetExec 节点处理一个或多个文件组
    • 每个文件组包含一个或多个 Parquet 文件路径。
    • ParquetExec 节点并行处理其组,顺序读取每个组的文件。
    • 输出是数据流到相应的 SortExec 节点。
  2. SortExec 节点并行地按 city(升序)和 time(降序)对数据进行排序。 SortPreservingMergeExec 计划需要排序。
  3. UnionExec 节点连接数据流以联合并行 SortExec 节点的输出。
  4. SortPreservingMergeExec 节点合并来自 UnionExec 的先前排序和联合的数据。

空结果集的 EXPLAIN 报告示例

如果您的表不包含查询时间范围内的数据,则物理计划以 EmptyExec 叶节点开始——例如

ProjectionExec: expr=[temp@0 as temp]
    SortExec: expr=[time@1 ASC NULLS LAST]
        EmptyExec: produce_one_row=false

分析前沿数据的查询计划

以下章节将指导您分析典型时间序列用例的物理查询计划——聚合最近写入的(前沿)数据。 尽管查询和计划比前面的示例更复杂,但您将遵循相同的步骤来读取查询计划。 在学习如何读取查询计划后,您将了解 ExecutionPlans、数据流和潜在的查询瓶颈。

示例数据

考虑以下 h20 数据,表示为写入 InfluxDB 的 Line Protocol “块”

// h20 data
// The following data represents 5 batches, or "chunks", of line protocol
// written to InfluxDB.
// - Chunks 1-4 are ingested and each is persisted to a separate partition file in storage.
// - Chunk 5 is ingested and not yet persisted to storage.
// - Chunks 1 and 2 cover short windows of time that don't overlap times in other chunks.
// - Chunks 3 and 4 cover larger windows of time and the time ranges overlap each other.
// - Chunk 5 contains the largest time range and overlaps with chunk 4, the Parquet file with the largest time-range.
// - In InfluxDB, a chunk never duplicates its own data.
//
// Chunk 1: stored Parquet file
// - time range: 50-249
// - no duplicates in its own chunk
// - no overlap with any other chunks
[
"h2o,state=MA,city=Bedford min_temp=71.59 150",
"h2o,state=MA,city=Boston min_temp=70.4, 50",
"h2o,state=MA,city=Andover max_temp=69.2, 249",
],

// Chunk 2: stored Parquet file
// - time range: 250-349
// - no duplicates in its own chunk
// - no overlap with any other chunks
// - adds a new field (area)
[
"h2o,state=CA,city=SF min_temp=79.0,max_temp=87.2,area=500u 300",
"h2o,state=CA,city=SJ min_temp=75.5,max_temp=84.08 349",
"h2o,state=MA,city=Bedford max_temp=78.75,area=742u 300",
"h2o,state=MA,city=Boston min_temp=65.4 250",
],

// Chunk 3: stored Parquet file
// - time range: 350-500
// - no duplicates in its own chunk
// - overlaps chunk 4
[
"h2o,state=CA,city=SJ min_temp=77.0,max_temp=90.7 450",
"h2o,state=CA,city=SJ min_temp=69.5,max_temp=88.2 500",
"h2o,state=MA,city=Boston min_temp=68.4 350",
],

// Chunk 4: stored Parquet file
// - time range: 400-600
// - no duplicates in its own chunk
// - overlaps chunk 3
[
 "h2o,state=CA,city=SF min_temp=68.4,max_temp=85.7,area=500u 600",
 "h2o,state=CA,city=SJ min_temp=69.5,max_temp=89.2 600",  // duplicates row 3 in chunk 5
 "h2o,state=MA,city=Bedford max_temp=80.75,area=742u 400", // overlaps chunk 3
 "h2o,state=MA,city=Boston min_temp=65.40,max_temp=82.67 400", // overlaps chunk 3
],

// Chunk 5: Ingester data
// - time range: 550-700
// - overlaps and duplicates data in chunk 4
[
"h2o,state=MA,city=Bedford max_temp=88.75,area=742u 600", // overlaps chunk 4
"h2o,state=CA,city=SF min_temp=68.4,max_temp=85.7,area=500u 650",
"h2o,state=CA,city=SJ min_temp=68.5,max_temp=90.0 600", // duplicates row 2 in chunk 4
"h2o,state=CA,city=SJ min_temp=75.5,max_temp=84.08 700",
"h2o,state=MA,city=Boston min_temp=67.4 550", // overlaps chunk 4
]

以下查询选择所有数据

SELECT state, city, min_temp, max_temp, area, time
FROM h2o
ORDER BY state asc, city asc, time desc;

输出如下

+-------+---------+----------+----------+------+--------------------------------+
| state | city    | min_temp | max_temp | area | time                           |
+-------+---------+----------+----------+------+--------------------------------+
| CA    | SF      | 68.4     | 85.7     | 500  | 1970-01-01T00:00:00.000000650Z |
| CA    | SF      | 68.4     | 85.7     | 500  | 1970-01-01T00:00:00.000000600Z |
| CA    | SF      | 79.0     | 87.2     | 500  | 1970-01-01T00:00:00.000000300Z |
| CA    | SJ      | 75.5     | 84.08    |      | 1970-01-01T00:00:00.000000700Z |
| CA    | SJ      | 68.5     | 90.0     |      | 1970-01-01T00:00:00.000000600Z |
| CA    | SJ      | 69.5     | 88.2     |      | 1970-01-01T00:00:00.000000500Z |
| CA    | SJ      | 77.0     | 90.7     |      | 1970-01-01T00:00:00.000000450Z |
| CA    | SJ      | 75.5     | 84.08    |      | 1970-01-01T00:00:00.000000349Z |
| MA    | Andover |          | 69.2     |      | 1970-01-01T00:00:00.000000249Z |
| MA    | Bedford |          | 88.75    | 742  | 1970-01-01T00:00:00.000000600Z |
| MA    | Bedford |          | 80.75    | 742  | 1970-01-01T00:00:00.000000400Z |
| MA    | Bedford |          | 78.75    | 742  | 1970-01-01T00:00:00.000000300Z |
| MA    | Bedford | 71.59    |          |      | 1970-01-01T00:00:00.000000150Z |
| MA    | Boston  | 67.4     |          |      | 1970-01-01T00:00:00.000000550Z |
| MA    | Boston  | 65.4     | 82.67    |      | 1970-01-01T00:00:00.000000400Z |
| MA    | Boston  | 68.4     |          |      | 1970-01-01T00:00:00.000000350Z |
| MA    | Boston  | 65.4     |          |      | 1970-01-01T00:00:00.000000250Z |
| MA    | Boston  | 70.4     |          |      | 1970-01-01T00:00:00.000000050Z |
+-------+---------+----------+----------+------+--------------------------------+

示例查询

以下查询从示例数据中选择前沿数据

SELECT city, count(1)
FROM h2o
WHERE time >= to_timestamp(200) AND time < to_timestamp(700)
  AND state = 'MA'
GROUP BY city
ORDER BY city ASC;

输出如下

+---------+-----------------+
| city    | COUNT(Int64(1)) |
+---------+-----------------+
| Andover | 1               |
| Bedford | 3               |
| Boston  | 4               |
+---------+-----------------+

前沿数据查询的 EXPLAIN 报告

以下查询为前面的示例查询生成 EXPLAIN 报告

EXPLAIN SELECT city, count(1)
FROM h2o
WHERE time >= to_timestamp(200) AND time < to_timestamp(700)
  AND state = 'MA'
GROUP BY city
ORDER BY city ASC;

前沿数据查询的 EXPLAIN 报告

示例数据中的注释告诉您哪些数据块重叠或重复其他数据块中的数据。 如果存在数据块中都存在数据的时间段,则两个数据块重叠。 您将在本指南的后面学习如何在查询计划中识别重叠和重复数据

与示例数据不同,您的数据可能不会告诉您重叠或重复存在的位置。 物理计划可以揭示数据中的重叠和重复,以及它们如何影响您的查询——例如,在学习如何读取物理计划后,您可以将数据扫描步骤总结如下

  • 查询执行从两个 ParquetExec 和一个 RecordBatchesExec 执行计划开始,这些计划并行运行。
  • 第一个 ParquetExec 节点读取两个不与任何其他文件重叠且不重复数据的文件; 这些文件不需要去重。
  • 第二个 ParquetExec 节点读取两个相互重叠的文件,并与 RecordBatchesExec 节点中扫描的摄取数据重叠; 查询计划必须在完成查询之前包含这些节点的去重过程。

其余部分分析示例物理计划中的 ExecutionPlan 节点结构和参数。 该示例包括 DataFusion 和 InfluxDB 特定的 ExecutionPlan 节点

定位物理计划

要开始分析查询的物理计划,请在EXPLAIN 报告中找到 plan_type 列值为 physical_plan 的行。 该行的 plan 列包含物理计划。

阅读物理计划

以下部分遵循读取查询计划的步骤,并检查物理计划节点及其输入和输出。

读取查询计划的执行流程,始终从最内层(叶)节点开始,向上读取到最外层的根节点。

物理计划叶节点

Query physical plan leaf node structures

物理计划中叶节点的结构

数据扫描节点 (ParquetExec 和 RecordBatchesExec)

示例物理计划包含三个叶节点——执行流程开始的最内层节点

由于 ParquetExecRecordBatchesExec 检索和扫描查询的数据,因此每个查询计划都以一个或多个这些节点开始。

ParquetExecRecordBatchesExec 节点的数量及其参数值可以告诉您为查询检索了哪些数据(以及多少数据),以及计划如何有效地处理数据的组织(例如,分区和去重)。

为方便起见,本指南使用名称 ParquetExec_AParquetExec_B 来表示示例物理计划中的 ParquetExec 节点。 从物理计划的顶部开始读取,ParquetExec_A 是物理计划中的第一个叶节点,ParquetExec_B 是最后一个(底部)叶节点。

这些名称指示节点在报告中的位置,而不是它们的执行顺序。

ParquetExec_A

ParquetExec: file_groups={2 groups: [[1/1/b862a7e9b329ee6a418cde191198eaeb1512753f19b87a81def2ae6c3d0ed237/243db601-f3f1-401b-afda-82160d8cc1a8.Parquet], [1/1/b862a7e9b329ee6a418cde191198eaeb1512753f19b87a81def2ae6c3d0ed237/f5fb7c7d-16ac-49ba-a811-69578d05843f.Parquet]]}, projection=[city, state, time], output_ordering=[state@1 ASC, city@0 ASC, time@2 ASC], predicate=time@5 >= 200 AND time@5 < 700 AND state@4 = MA, pruning_predicate=time_max@0 >= 200 AND time_min@1 < 700 AND state_min@2 <= MA AND MA <= state_max@3                                           |

ParquetExec_A,第一个 ParquetExec 节点

ParquetExec_A 具有以下特征

file_groups

文件组是运算符要读取的文件列表。 文件通过路径引用

  • 1/1/b862a7e9b.../243db601-....parquet
  • 1/1/b862a7e9b.../f5fb7c7d-....parquet

路径结构表示数据的组织方式。 您可以使用文件路径来收集有关查询的更多信息——例如

  • 在目录中查找文件信息(例如:大小和行数)
  • 从对象存储下载 Parquet 文件以进行调试
  • 查找查询读取的分区数

路径具有以下结构

<namespace_id>/<table_id>/<partition_hash_id>/<uuid_of_the_file>.Parquet
    1         /    1    /b862a7e9b329ee6a4.../243db601-f3f1-4....Parquet
  • namespace_id:正在查询的命名空间(数据库)
  • table_id:正在查询的表(测量)
  • partition_hash_id:此文件所属的分区。 您可以计算分区 ID 以查找查询读取的分区数。
  • uuid_of_the_file:文件标识符。

ParquetExec 并行处理组,并顺序读取每个组中的文件。

file_groups={2 groups: [[1/1/b862a7e9b329ee6a4/243db601....parquet], [1/1/b862a7e9b329ee6a4/f5fb7c7d....parquet]]}
  • {2 groups: [[file], [file]}:ParquetExec_A 接收两个组,每个组包含一个文件。 因此,ParquetExec_A 并行读取两个文件。
projection

projection 列出了 ExecutionPlan 要读取和输出的表列。

projection=[city, state, time]
output_ordering

output_ordering 指定 ExecutionPlan 输出的排序顺序。 如果输出应排序并且计划器知道顺序,则查询计划器会传递此参数。

output_ordering=[state@2 ASC, city@1 ASC, time@3 ASC, __chunk_order@0 ASC]

在将数据存储到 Parquet 文件时,InfluxDB 会对数据进行排序以提高存储压缩和查询效率,并且计划器会尝试尽可能长时间地保留该顺序。 通常,ParquetExec 接收的 output_ordering 值是存储数据的排序(或排序的子集)。

根据设计,RecordBatchesExec 数据未排序。

在示例中,计划器指定 ParquetExec_A 使用现有的排序顺序 state ASC, city ASC, time ASC, 进行输出。

要查看存储数据的排序顺序,请为 SELECT ALL 查询生成 EXPLAIN 报告——例如

EXPLAIN SELECT * FROM TABLE_NAME WHERE time > now() - interval '1 hour'

如果查询返回的数据过多,请缩短时间范围。

predicate

predicate 是查询中指定的数据过滤器。

predicate=time@5 >= 200 AND time@5 < 700 AND state@4 = MA
pruning predicate

pruning_predicate 是从 predicate 值创建的,并且是实际用于从选定分区中剪枝数据和文件的谓词。 默认过滤器按 time 过滤文件。

pruning_predicate=time_max@0 >= 200 AND time_min@1 < 700 AND state_min@2 <= MA AND MA <= state_max@3

在生成物理计划之前,一个额外的 partition pruning 步骤使用分区列上的谓词来剪枝分区。

RecordBatchesExec

RecordBatchesExec: chunks=1, projection=[__chunk_order, city, state, time]
RecordBatchesExec

RecordBatchesExec 是 InfluxDB 特定的 ExecutionPlan 实现,用于从摄取器中检索最近写入但尚未持久化的数据。

在示例中,RecordBatchesExec 包含以下表达式

chunks

chunks 是从摄取器接收的数据块的数量。

chunks=1
  • chunks=1RecordBatchesExec 接收一个数据块。
projection

projection 列表指定节点要读取和输出的列或表达式。

[__chunk_order, city, state, time]

数据扫描节点中 __chunk_order 的存在表明节点之间存在数据重叠,并且可能重复。

ParquetExec_B

示例物理计划中的底部叶节点是另一个 ParquetExec 运算符,ParquetExec_B

ParquetExec_B 表达式
ParquetExec:
  file_groups={2 groups: [[1/1/b862a7e9b.../2cbb3992-....Parquet],
   [1/1/b862a7e9b.../9255eb7f-....Parquet]]},
  projection=[__chunk_order, city, state, time],
  output_ordering=[state@2 ASC, city@1 ASC, time@3 ASC, __chunk_order@0 ASC],
  predicate=time@5 >= 200 AND time@5 < 700 AND state@4 = MA,
  pruning_predicate=time_max@0 >= 200 AND time_min@1 < 700 AND state_min@2 <= MA AND MA <= state_max@3
ParquetExec_B,第二个 ParquetExec

由于 ParquetExec_B 存在重叠,因此 projectionoutput_ordering 表达式使用 RecordBatchesExec projection 中使用的 __chunk_order 列。

数据扫描节点中 __chunk_order 的存在表明节点之间存在数据重叠,并且可能重复。

其余 ParquetExec_B 表达式与 ParquetExec_A 中的表达式类似。

查询计划如何分发数据以进行扫描

如果您比较file_group路径在 ParquetExec_AParquetExec_B 中的路径,您会注意到两者都包含来自同一分区的文件

1/1/b862a7e9b329ee6a4.../...

计划器可能会将来自同一分区的文件分发到不同的扫描节点,原因有多种,包括处理重叠的优化——例如

  • 将非重叠文件与重叠文件分开,以最大限度地减少去重所需的工作量(在本示例中就是这种情况)
  • 分发非重叠文件以增加并行执行

分析分支结构

从数据扫描节点输出数据后,数据会向上流到下一个父(外部)节点。

在示例计划中

  • 每个叶节点都是为处理扫描数据而计划的节点分支中的第一步。
  • 这三个分支并行执行。
  • 在叶节点之后,每个分支都包含以下类似的节点结构
...
CoalesceBatchesExec: target_batch_size=8192
    FilterExec: time@3 >= 200 AND time@3 < 700 AND state@2 = MA
    ...
  • FilterExec: time@3 >= 200 AND time@3 < 700 AND state@2 = MA:为条件 time@3 >= 200 AND time@3 < 700 AND state@2 = MA 过滤数据,并保证所有数据都被剪枝。
  • CoalesceBatchesExec: target_batch_size=8192:将小批量合并为大批量。 请参阅 DataFusion [CoalesceBatchesExec] 文档。

排序尚未持久化的数据

RecordBatchesExec 分支中,CoalesceBatchesExec 之后的节点是 SortExec 节点

SortExec: expr=[state@2 ASC,city@1 ASC,time@3 ASC,__chunk_order@0 ASC]

该节点使用指定的表达式 state ASC, city ASC, time ASC, __chunk_order ASC 对尚未持久化的数据进行排序。 ParquetExec_A 和 ParquetExec_B 都不包含类似的节点,因为对象存储中的数据已经按给定顺序排序(由 摄取器压缩器); 查询计划只需要对来自摄取器的数据进行排序。

识别重叠和重复数据

在物理计划示例中,ParquetExec_B 和 RecordBatchesExec 节点共享以下父节点

...
DeduplicateExec: [state@2 ASC,city@1 ASC,time@3 ASC]
    SortPreservingMergeExec: [state@2 ASC,city@1 ASC,time@3 ASC,__chunk_order@0 ASC]
        UnionExec
            ...
重叠数据节点结构
  1. UnionExec:通过连接分区来联合多个输入数据流。UnionExec 不执行任何合并操作,执行速度很快。
  2. SortPreservingMergeExec: [state@2 ASC,city@1 ASC,time@3 ASC,__chunk_order@0 ASC]:合并已排序的数据;表明先前的数据(来自其下方的节点)已排序。输出数据是单个排序流。
  3. DeduplicateExec: [state@2 ASC,city@1 ASC,time@3 ASC]:对已排序的输入数据流进行去重。由于 SortPreservingMergeExec 确保单个排序流,因此它通常(但并非总是)在 DeduplicateExec 之前。

DeduplicateExec 节点表明包含的节点具有重叠数据——文件或批次中的数据与另一个文件或批次中的数据的时间戳范围相同。由于 InfluxDB 组织数据的方式,数据永远不会在文件内部重复。

在示例中,DeduplicateExec 节点包含 ParquetExec_B 和 RecordBatchesExec 节点,这表明 ParquetExec_B 文件组 文件与尚未持久化的数据重叠。

以下示例数据摘录显示了文件和 Ingester 数据之间的重叠数据

// Chunk 4: stored Parquet file
//   - time range: 400-600
[
 "h2o,state=CA,city=SF min_temp=68.4,max_temp=85.7,area=500u 600",
],

// Chunk 5: Ingester data
//   - time range: 550-700
//   - overlaps and duplicates data in chunk 4
[
"h2o,state=MA,city=Bedford max_temp=88.75,area=742u 600", // overlaps chunk 4
...
"h2o,state=MA,city=Boston min_temp=67.4 550", // overlaps chunk 4
]

如果文件或摄取的数据重叠,则 Querier 必须在查询计划中包含 DeduplicateExec 以删除任何重复项。DeduplicateExec 不一定表示数据重复。如果计划读取许多文件并对所有文件执行去重,则可能是出于以下原因

  • 文件包含重复数据
  • 对象存储中有许多 Compactor 尚未压缩的小型重叠文件。压缩后,您的查询性能可能会更好,因为它需要读取的文件更少
  • Compactor 跟不上进度。如果数据未重复,并且压缩后仍然有许多小型重叠文件,那么您可能需要检查 Compactor 的工作负载并根据需要添加更多资源

分支中没有 DeduplicateExec 节点的叶节点不需要去重,也不会与其他文件或 Ingester 数据重叠——例如,ParquetExec_A 没有重叠

ProjectionExec:...
    CoalesceBatchesExec:...
        FilterExec:...
            ParquetExec:...

缺少 DeduplicateExec 节点意味着文件不重叠。

数据扫描输出

ProjectionExec 节点过滤列,以便输出中仅保留 city

`ProjectionExec: expr=[city@0 as city]`
最终处理

在每个叶节点中去重和过滤数据后,计划将合并输出,然后应用聚合和排序运算符以获得最终结果

| physical_plan | SortPreservingMergeExec: [city@0 ASC NULLS LAST]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
|               |   SortExec: expr=[city@0 ASC NULLS LAST]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
|               |     AggregateExec: mode=FinalPartitioned, gby=[city@0 as city], aggr=[COUNT(Int64(1))]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
|               |       CoalesceBatchesExec: target_batch_size=8192                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
|               |         RepartitionExec: partitioning=Hash([city@0], 4), input_partitions=4                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
|               |           AggregateExec: mode=Partial, gby=[city@0 as city], aggr=[COUNT(Int64(1))]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
|               |             RepartitionExec: partitioning=RoundRobinBatch(4), input_partitions=3                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
|               |               UnionExec

用于聚合、排序和最终输出的运算符结构。

  • UnionExec:联合数据流。请注意,输出流的数量与输入流的数量相同——UnionExec 节点是下游运算符的中间步骤,下游运算符实际合并或拆分数据流。
  • RepartitionExec: partitioning=RoundRobinBatch(4), input_partitions=3:以轮询方式将三个输入流拆分为四个输出流。该计划拆分流以提高并行执行。
  • AggregateExec: mode=Partial, gby=[city@0 as city], aggr=[COUNT(Int64(1))]:按照查询中指定的进行数据分组:city, count(1)。此节点分别聚合四个流中的每一个,然后输出四个流,由 mode=Partial 指示——数据未完全聚合。
  • RepartitionExec: partitioning=Hash([city@0], 4), input_partitions=4:在 Hash([city]) 上重新分区数据,并分成四个流——每个流包含一个城市的数据。
  • AggregateExec: mode=FinalPartitioned, gby=[city@0 as city], aggr=[COUNT(Int64(1))]:对数据应用最终聚合 (aggr=[COUNT(Int64(1))])。mode=FinalPartitioned 表示数据已经分区(按城市),不需要 AggregateExec 进一步分组。
  • SortExec: expr=[city@0 ASC NULLS LAST]:按照查询中的指定,对四个数据流进行排序,每个流都按 city 排序。
  • SortPreservingMergeExec: [city@0 ASC NULLS LAST]:合并和排序四个已排序的流,以获得最终输出。

在前面的示例中,EXPLAIN 报告显示了查询计划,但未执行查询。要查看运行时指标,例如计划及其运算符的执行时间,请使用 EXPLAIN ANALYZE 生成报告,并在必要时使用 跟踪 进行进一步调试。


此页内容是否对您有帮助?

感谢您的反馈!


Flux 的未来

Flux 即将进入维护模式。您可以继续像现在一样使用它,而无需对您的代码进行任何更改。

阅读更多

InfluxDB 3 开源版本现已发布公开 Alpha 版

InfluxDB 3 开源版本现已可用于 Alpha 测试,根据 MIT 或 Apache 2 许可获得许可。

我们正在发布两个产品作为 Alpha 版的一部分。

InfluxDB 3 Core 是我们的新开源产品。它是一个用于时间序列和事件数据的最新数据引擎。InfluxDB 3 Enterprise 是一个商业版本,它建立在 Core 的基础上,增加了历史查询能力、读取副本、高可用性、可扩展性和细粒度的安全性。

有关如何入门的更多信息,请查看