在 InfluxDB 3.0 中使用渐进式评估优化查询
作者:Nga Tran / Reid Kaufmann / 开发者
2024 年 11 月 14 日
导航至
在之前的文章中,我们描述了一种技术,该技术使“最新值”查询速度提高数百倍,并使我们的许多客户受益。这项技术背后的思想是逐步评估按时间组织的文件,直到我们找到最新值。从那时起,我们收到了诸如“哪些查询支持渐进式评估?”、“我们如何验证查询是否经过渐进式评估?”、“是否存在渐进式评估无济于事的文件组织?”之类的问题。这篇博文回答了这些问题。
支持渐进式评估的查询
目前,此技术仅适用于 SQL 查询;它尚不适用于 InfluxQL 查询。您的 SQL 查询必须包含子句 ORDER BY time DESC
(或 ASC
)。此外,其他一些限制包括 表达式、别名(包括 AT TIME ZONE
)以及 SELECT
子句上的聚合。换句话说,SELECT
子句中的所有内容都必须是简单的表列。
支持的查询示例
SELECT host, temperature
FROM machine
WHERE time > now - interval ‘1 day’ and region = ‘US’
ORDER BY time ASC;
SELECT host, temperature
FROM machine
WHERE time > now - interval ‘1 day’ and region = ‘US’
ORDER BY time DESC
LIMIT 10;
不支持的查询示例
这些查询尚未使用渐进式评估进行优化。我们希望在未来的版本中解除这些限制。
带有表达式的查询 (temperature + 2
)
SELECT host, temperature + 2
FROM machine
WHERE time > now - interval ‘1 day’ and region = ‘US’
ORDER BY time ASC;
带有别名的查询 (as host_name
)
SELECT host as host_name, time
FROM machine
WHERE time > now - interval ‘1 day’ and region = ‘US’
ORDER BY time ASC;
指定时区然后指定别名的查询 (AT TIME ZONE ‘Europe/Oslo’ as time)
SELECT host, time AT TIME ZONE ‘Europe/Oslo’ as time
FROM machine
WHERE time > now - interval ‘1 day’ and region = ‘US’
ORDER BY time DESC
LIMIT 10;
带有聚合的查询 (min(temperature))
SELECT min(temperature)
FROM machine
WHERE time > now - interval ‘1 day’ and region = ‘US’
ORDER BY time DESC
LIMIT 10;
查询正在进行渐进式评估的信号
如果您的查询计划中包含 ProgressiveEvalExec
,则表示它已使用渐进式评估进行了优化(有关如何获取和阅读查询计划,请参阅这篇文章)。但是,缺少渐进式评估并不意味着您的查询运行速度会很慢。我们仅在它实际有益于您的查询时才应用它。
从渐进式评估中受益的文件组织
要了解渐进式评估何时有益于您的查询,我们首先需要了解数据组织,这是影响查询性能的最重要因素之一。
InfluxDB 3.0 中的数据组织
以下是 InfluxDB 3.0 中数据组织方式的简要说明(有关完整的数据周期、数据压缩方式以及它如何提高查询性能,请参阅我们的系统架构和数据压缩)。
作为时间序列数据库,表 machine
中的数据始终包含代表事件时间的 time
列,例如 UTC 上午 9:30 的温度。图 1 显示了数据组织的三种不同阶段。图中的每个矩形都表示一个数据块。C 表示尚未持久化的数据,通常包括最新值。L 表示不同持久化文件的级别。L0 用于新摄取和持久化数据的文件。它们通常很小,并且包含最近的值。但是,回填数据的 L0 文件可能与所需的时间一样旧。L1 文件存储压缩许多小型 L0 文件的结果。我们还有 L2 文件,但它们超出了本主题的范围,并且不会改变渐进式评估的工作方式。
图 1(借用自压缩博文):两次压缩后的数据组织四个阶段。
在阶段 1 中,所有数据都位于小型 L0 文件中。在阶段 2 中,阶段 1 中的数据已压缩为较大的 L1 文件,而一些新数据持久化在一些小型 L0 文件中,而一些数据位于尚未持久化的块 (C) 中。如果您大部分时间都在摄取新数据,则您的数据组织主要看起来像阶段 2 或阶段 3。但是,如果您回填数据,则您的数据组织可以结合阶段 1 和阶段 2 或阶段 3。因此,根据您如何摄取数据以及压缩器跟上您的摄取工作负载的速度,可能会存在少量或大量重叠的小文件。阶段 3 是我们所说的“良好压缩数据”,通常最适合查询性能。压缩器的目标是将您的大部分数据存储在阶段 3 中。避免频繁回填数据也有助于保持您的数据良好压缩。
渐进式评估在各种重叠场景中的应用
让我们回顾一下查询不同数据集的示例。图 2 显示了表 machine
的数据组织。我们使用 F 作为文件的前缀名称,它可以是 L0 或 L1。文件 F1、F2、F6 和 F7 与任何文件都没有时间重叠。文件 F3、F4 和 F5 彼此重叠,文件 F8 与 F9 重叠,F9 又与块 C 重叠。
图 2:表 machine
的数据组织
仅读取非重叠文件
如果您的查询要求 t1 之前的最新数据
SELECT temperature
FROM machine
WHERE time < t1 and region = ‘US’
ORDER BY time DESC LIMIT 1;
该查询将通过渐进式评估进行优化,因为所需的文件 F1 和 F2 不重叠。简化的查询计划如下
ProgressiveEvalExec: fetch=1
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[true]
ParquetExec: file_groups={2 groups: [F2], [F1]}
要使 ProgressiveEvalExec
正常工作,查询计划中需要一些基本属性
ParquetExec
中的文件按时间降序排序 F2、F1。preserve_partitioning=[true]
表示 2 个文件组 [F2] 和 [F1] 的数据在其自己的组中排序,并且不会合并。这对于我们能够在获取 F1 之前从 F2 获取数据非常重要。fetch=1
表示查询会在获得满足查询过滤条件的行时立即停止运行。换句话说,如果文件 F2 中至少有一行以US
作为区域,则永远不会读取 F1。
按时间升序排序的相同查询将如下所示
ProgressiveEvalExec: fetch=1
SortExec: TopK(fetch=1), expr=[time ASC], preserve_partitioning=[true]
ParquetExec: file_groups={2 groups: [F1], [F2]}
如果您的查询读取 t2 和 t3 之间仅包含非重叠文件的数据,您将获得类似的查询计划。
仅读取重叠文件
现在让我们看一下读取 t1 和 t2 之间数据的查询。
SELECT temperature
FROM machine
WHERE time < t2 and time > t1 and region = ‘US’
ORDER BY time DESC LIMIT 1;
由于所有三个所需文件 F3、F4 和 F5 重叠,我们需要合并数据以进行重复数据删除,并且它们无法逐步逐个评估。简化的查询计划将如下所示
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[false]
DeduplicateExec:
SortPreservingMergeExec:
ParquetExec: file_groups={3 groups: [F4], [F3], [F5]}
在没有 ProgressiveEvalExec
的情况下,ParquetExec
中的文件可以按任何顺序和组排列,因为这些组将并行读取并合并为一个流以进行重复数据删除。
同样,如果您的查询读取 t3 之后重叠的数据,则不会应用渐进式评估。
读取非重叠文件和重叠文件的混合
当您的查询读取非重叠数据和重叠数据的混合时,将应用渐进式评估,并且数据会相应地拆分和分组。让我们看一下读取 t2 之前数据的查询。
SELECT temperature
FROM machine
WHERE time < t2 and region = ‘US’
ORDER BY time DESC LIMIT 1;
简化的查询计划将如下所示
ProgressiveEvalExec: fetch=1
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[false]
DeduplicateExec:
SortPreservingMergeExec:
ParquetExec: file_groups={3 groups: [F4], [F3], [F5]}
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[true]
ParquetExec: file_groups={2 groups: [F2], [F1]}
即使文件 F3、F4 和 F5 重叠,它们也不与 F1 和 F2 重叠,并且包含更新的数据。因此,F3、F4 和 F5 的子计划通过 F2 和 F1 进行渐进式评估。请注意,ProgressiveEvalExec
的输入流数为三个:一个用于 F3、F4 和 F5 的合并,一个用于 F2,一个用于 F1。这三个流按该顺序逐步评估。
如果查询按升序排序,则渐进顺序将相反
ProgressiveEvalExec: fetch=1
SortExec: TopK(fetch=1), expr=[time ASC], preserve_partitioning=[true]
ParquetExec: file_groups={2 groups: [F1], [F2]}
SortExec: TopK(fetch=1), expr=[time ASC], preserve_partitioning=[false]
DeduplicateExec:
SortPreservingMergeExec:
ParquetExec: file_groups={3 groups: [F4], [F3], [F5]}
三个流仍然进入 ProgressiveEvalExec
,但顺序相反:F1、F2 以及 F3、F4 和 F5 合并。
同样,让我们读取所有数据
SELECT temperature
FROM machine
WHERE time < now and region = ‘US’
ORDER BY time DESC LIMIT 1;
查询计划变得更加复杂,但遵循相同的规则:重叠数据的子计划将按正确的顺序排列,并与非重叠文件进行渐进式评估。
ProgressiveEvalExec: fetch=1
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[false]
DeduplicateExec:
SortPreservingMergeExec:
SortExec:
RecordBatchExec: {C}
ParquetExec: file_groups={2 groups: [F8], [F9]}
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[true]
ParquetExec: file_groups={2 groups: [F7], [F6]}
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[false]
DeduplicateExec:
SortPreservingMergeExec:
ParquetExec: file_groups={3 groups: [F4], [F3], [F5]}
SortExec: TopK(fetch=1), expr=[time DESC], preserve_partitioning=[true]
ParquetExec: file_groups={2 groups: [F2], [F1]}
在此顺序中,ProgressiveEvalExec
逐步评估了六个非重叠数据流
- C、F8 和 F9 的合并
- F7
- F6
- F3、F4 和 F5 的合并
- F2
- F1
如果您的查询按时间升序对数据进行排序,您将获得类似的查询计划,但顺序相反。
缓存影响:渐进式评估可能有助于其他查询的延迟
对于高度依赖缓存文件以获得尽可能最低延迟但运行接近缓存限制的工作负载,渐进式评估可以带来显着的性能差异。显然,不遍历额外的 Parquet 文件可以减少优化查询的 CPU 时间。根据时间范围与 LIMIT 范围相比的过度程度,数据库可能将更少的文件带入缓存这一事实意味着其他查询的文件无需被逐出,从而可能减少系统上其他查询的延迟。
总结
如果您的查询选择纯表列并按时间对数据进行排序,即使查询数据的子集包含非重叠文件,InfluxDB 3.0 也会自动使用渐进式评估来提高您的查询性能。当您的所有数据都重叠时,无法使用渐进式评估。