系统表第 2 部分:我们如何使其更快

导航至

第一篇文章中,我们介绍了系统表以及如何使用它们来检查您的集群。在本文中,我们将解释一些提高系统表查询速度的技术。

7. 问题

在 2024 年 8 月之前,在 Cloud Dedicated 中查询系统表,特别是 system.tablessystem.partitionssystem.partitions,通常需要很长时间才能运行,即使应用了过滤器也是如此。在某些情况下,查询超时而没有返回任何结果。

8. 为什么速度慢

为了理解查询缓慢的原因,让我们首先看一下系统表数据是如何生成的。

下面是 InfluxDB 3.0 架构的简化概述(引自博客 InfluxDB 3.0:系统架构)。中心组件 Catalog1 存储关于数据库、表、列和文件详细信息(如文件大小、位置、创建时间等)的元数据。在右侧,我们有 Querier,系统表驻留在此处并执行查询。 图 1:InfluxDB 3.0 架构

Catalog 中的元数据按照下面显示的简化数据模型(图 2)组织: 图 2:简化的 Catalog 元数据数据模型

由于 InfluxDB Catalog 充当中心集群协调器,它提供了受限的接口,为了性能和稳定性原因,不允许进行分析式查询。因此,Querier 必须多次调用 Catalog 才能收集填充系统表所需的信息。图 3 说明了查询像 system.partitions 这样的系统表时,典型的数据流是什么样的。

system.partitions 表有一列显示分区总大小(以兆字节为单位)。为了计算这一点,Querier 首先获取数据库中的所有表,然后检索每个表的所有分区,并查找与每个分区关联的每个 Parquet 文件。之后,它将文件大小相加并将总大小转换为兆字节。此过程涉及 Querier 和 Catalog 之间的多个请求,因为它需要多次数据检索,如图 3 所示。 图 3:Querier 和 Catalog 之间的数据流

8.1 性能问题

以前,当查询像 system.tablessystem.partitionssystem.compactor 这样的系统表时,Catalog 会扫描所有元数据并通过 gRPC 格式将其发送到 Querier。然后,Querier 会将响应转换为 Arrow 记录批次,并使用 DataFusion 运行查询,DataFusion 会应用过滤器来丢弃不相关的数据。

这意味着像这样的查询

SELECT * FROM system.partitions;
SELECT * FROM system.partitions WHERE table_name = 'foo';

即使第二个查询有过滤器,Catalog 和 Querier 中的工作量也相同。系统扫描所有内容,在 Querier 级别应用过滤器,导致不必要的开销和缓慢的性能。

9. 解决方案

9.1 谓词下推

谓词是用于在查询中过滤数据的条件,例如 table_name = ‘foo’age > 20。我们实现了谓词下推,这是一种常见的数据库优化技术,将某些过滤器(谓词)尽可能地移动(或“下推”)到数据源(即,Catalog 内部),如图 4 所示。此更改减少了提取和传输到 Querier 的数据量,从而减少了 Querier 的工作负载。 图 4:谓词下推

在我们实现谓词下推之前,像 SELECT * FROM system.partitions WHERE table_name = 'foo' 这样的查询会获取并格式化 Catalog 中所有表的分区信息,并且查询引擎会立即丢弃除 foo 之外的所有内容。

在谓词下推之后,querier 避免获取和格式化它确定将在查询执行期间被过滤掉的分区信息。

9.2 多谓词下推

除了支持单谓词下推之外,我们还扩展了支持以处理多个过滤器。第 9.1 节中的简单示例可能很明显,但在实际系统中,用户可以提供由 ANDORIN 等连接的任意谓词,并且确定要下推哪些谓词并非易事。例如,考虑如下查询:

SELECT * 
FROM system.partitions 
WHERE (table_name = 'foo' OR table_name = 'bar')
  AND (partition_key = '2024-10|device-101' OR partition_key = '2024-09|device-101')

我们使用 DataFusion 的 LiteralGuarantee::analyze 来解析和简化查询谓词,然后再将它们下推到 Catalog。此方法为我们节省了大量的工程时间和精力。非常感谢 DataFusion 社区使其如此易于使用!

当前的实现支持多谓词下推,用于诸如 table_namepartition_keytable_namepartition_id 组合的过滤器,进一步减少了 Querier 处理的数据量。

9.3 并发数据获取

以前,Querier 顺序地向 Catalog 发出 API 调用,其中每个请求都必须等待前一个请求完成才能继续。这增加了显著的延迟,尤其是在查询大型数据集时。

我们通过启用并发 API 请求改进了这一点,允许 Querier 同时发出多个请求。这大大减少了收集所有必要元数据所需的时间。

10. 性能改进

以下是使用过滤器 WHERE table_name 时,系统表查询速度的提升幅度

  1. system.tables:快 17%
  2. system.partitions:快 65%
  3. system.compactor:快 60%

这些改进是基于一个包含 100 个表、超过 200 个分区和 3,000 多个 Parquet 文件的数据库。如果您的数据库有更多的表、分区或 Parquet 文件,您将在过滤查询中看到更显著的性能提升。

此外,对于像这样的查询,具有多个过滤器的平均查询延迟约为 20 毫秒

WHERE table_name = '...' AND partition_key = '...'
WHERE table_name = '...' AND partition_id = ...

11. 总结

在这篇文章中,我们解释了如何通过实现谓词下推、优化多过滤器的使用以及启用并发数据获取来提高系统表查询的性能。这些更改显著减少了查询时间,特别是对于具有大量元数据的数据库。

通过这些优化,您可以更有效地检索相关的系统数据,并消除漫长的调试等待时间。

参考文献


  1. 请注意,在我们实际的部署中,有几个缓存层未在图 1 中反映出来。