菜单
文档面包屑箭头 Grafana Loki面包屑箭头 管理面包屑箭头 布隆过滤器
开源

管理布隆过滤器构建和查询(实验性)

警告

在 Loki 和 Grafana Enterprise Logs (GEL) 中,使用布隆过滤器进行查询加速是一个实验性功能。不提供工程和值班支持。不提供 SLA。请注意,此功能适用于每月摄取超过 75TB 日志的用户,因为它旨在加速对大容量日志的查询。

在 Grafana Cloud 中,针对每月摄取超过 75TB 日志的部分大型客户,使用布隆过滤器加速查询功能已作为公共预览版启用。提供有限支持,但不提供 SLA。

Loki 利用布隆过滤器通过减少 Loki 需要从存储中加载和迭代的数据量来加速查询。Loki 通常用于运行“大海捞针”式查询;这些查询搜索大量日志行,但只有少量日志行匹配查询。一些常见的用例是搜索与特定追踪 ID 或客户 ID 相关的所有日志。

此类查询的一个示例是查找过去 24 小时内整个集群上的追踪 ID

logql
{cluster="prod"} | traceID="3c0e3dcd33e7"

如果不进行加速过滤,Loki 将下载与 {cluster="prod"} 匹配的所有流在过去 24 小时内的所有块,并迭代每个块中的每行日志,检查是否存在结构化元数据traceID 及其值 3c0e3dcd33e7

通过加速过滤,Loki 能够跳过大部分块,仅处理那些我们有统计信心认为结构化元数据对可能存在的块。

要了解如何编写使用布隆过滤器的查询,请参阅查询加速

启用布隆过滤器

警告

出于设计考虑,单体二进制部署不支持构建和查询布隆过滤器。它可以与 Simple Scalable deployment (SSD) 一起使用,但建议仅在完全分布式的微服务模式下运行布隆组件。原因是构建和查询布隆过滤器也会带来相对较高的成本,只有在大规模部署中才能显现其优势。

要开始构建和使用布隆过滤器,您需要:

yaml
# Configuration block for the bloom creation.
bloom_build:
  enabled: true
  planner:
    planning_interval: 6h
  builder:
    planner_address: bloom-planner.<namespace>.svc.cluster.local.:9095

# Configuration block for bloom filtering.
bloom_gateway:
  enabled: true
  client:
    addresses: dnssrvnoa+_bloom-gateway-grpc._tcp.bloom-gateway-headless.<namespace>.svc.cluster.local

# Enable blooms creation and filtering for all tenants by default
# or do it on a per-tenant basis.
limits_config:
  bloom_creation_enabled: true
  bloom_split_series_keyspace_by: 1024
  bloom_gateway_enable_filtering: true

有关更多配置选项,请参阅 Bloom GatewayBloom Build每个租户限制的配置文档。我们强烈建议在使用此实验性功能之前完整阅读相关文档。

Bloom Planner 和 Builder

从对象存储中的块构建布隆过滤器由两个组件完成:Bloom Planner 和 Bloom Builder,其中 Planner 为布隆构建创建任务,并将任务发送给 Builder 进行处理并上传生成的块。布隆过滤器按组存储在布隆块中,一个块跨越给定日期的多个流(也称为系列)和块。要了解有关块和元数据文件如何组织的更多信息,请参阅下面的构建布隆过滤器部分。

Bloom Planner 作为单个实例运行,计算需要构建布隆过滤器的租户在特定时间段内的指纹范围间隔。它将这些任务分派给可用的 Builder。Planner 还负责应用布隆过滤器保留策略

警告

不要运行多于一个 Bloom Planner 实例。

Bloom Builder 是一个无状态的可横向扩展组件,可以独立于 Planner 进行扩展,以满足创建任务的处理需求。

您可以在Bloom Builder 的配置部分找到这些组件的所有配置选项。请参阅上面的启用布隆过滤器部分,获取启用此功能的配置片段。

保留

Bloom Planner 在对象存储上应用布隆块保留策略。默认情况下禁用保留。启用后,保留策略将应用于所有租户。每个租户的保留期取其配置的通用保留期(retention_period)和流保留期(retention_stream)中最长的时间。

例如,在以下示例中,租户 A 的布隆过滤器保留期为 30 天,租户 B 对于 {namespace="prod"} 流的布隆过滤器保留期为 40 天。

yaml
overrides:
    "A":
        retention_period: 30d
    "B":
        retention_period: 30d
        retention_stream:
            - selector: '{namespace="prod"}'
              priority: 1
              period: 40d

Planner 和 Builder 的规模调整和配置

单个 Planner 实例在给定间隔内为每个租户运行布隆块的规划阶段,并将创建的任务放入内部任务队列。Builder 通过从队列中拉取任务来顺序处理任务。完成所有待处理任务所需的 Builder 副本数量在下一次规划迭代之前取决于 -bloom-build.planner.bloom_split_series_keyspace_by 的值、租户数量以及流的日志量。

通过 -bloom-build.max-block-size 为每个租户配置最大块大小。考虑到我们将流的布隆过滤器追加到块中,直到块大于配置的最大大小,实际块大小可能会超过此限制。块在内存中创建,一旦写入对象存储,它们就会被释放。块和 TSDB 文件从对象存储下载到文件系统。我们估计每个核心每秒能够处理 4MB 的数据。

Bloom Gateway

Bloom Gateway 处理来自索引网关的块过滤请求。该服务接收块列表和过滤表达式,并将它们与布隆过滤器进行匹配,过滤掉不匹配给定过滤表达式的块。

此组件是可横向扩展的,每个实例仅拥有其执行过滤的流指纹范围的一个子集。数据分片是在客户端使用服务器实例的 DNS 发现和用于一致性哈希及流指纹在 Bloom Gateway 实例间均匀分布的jumphash 算法进行的。

您可以在Bloom Gateway 的配置部分找到此组件的所有配置选项。请参阅上面的启用布隆过滤器部分,获取启用此功能的配置片段。

Gateway 的规模调整和配置

Bloom Gateway 使用其本地文件系统作为从对象存储下载的布隆过滤器的最近最少使用 (LRU) 缓存。布隆过滤器的大小取决于摄取量和唯一的结构化元数据键值对的数量,以及布隆过滤器的构建设置,即误报率。在默认设置下,布隆过滤器的大小小于原始结构化元数据大小的 1%。

由于读取布隆过滤器严重依赖磁盘 IOPS,Bloom Gateway 应利用多个本地连接的 SSD 磁盘 (NVMe) 来提高 I/O 吞吐量。使用逗号分隔的挂载点列表时,可以使用 -bloom.shipper.working-directory 设置指定不同磁盘挂载上的多个目录,例如:

yaml
-bloom.shipper.working-directory="/mnt/data0,/mnt/data1,/mnt/data2,/mnt/data3"

Bloom Gateway 需要处理相对较大的文件:布隆过滤器块。尽管布隆块的二进制格式允许将其按较小的页读入内存,但内存消耗取决于同时加载到内存中进行处理的页数。以下三个设置的乘积控制着任意给定时间内布隆过滤器数据在内存中的最大量:-bloom-gateway.worker-concurrency-bloom-gateway.block-query-concurrency-bloom.max-query-page-size

示例,假设有 4 个 CPU 核

yaml
-bloom-gateway.worker-concurrency=4      // 1x NUM_CORES
-bloom-gateway.block-query-concurrency=8 // 2x NUM_CORES
-bloom.max-query-page-size=64MiB

4 x 8 x 64MiB = 2048MiB

这里,块处理的内存需求是 2GiB。要获取 Bloom Gateway 的最低要求,需要将此值翻倍。

构建布隆过滤器

布隆过滤器按流构建,并聚合成块文件。流根据其指纹分配到块中,遵循与 Loki 的 TSDB 和分片计算相同的排序方案。这在查询时提供了数据局部性优势,因为同一分片中的流可能位于同一块中。

除了块之外,Builder 还维护一个元数据文件列表,其中包含对布隆块以及构建这些布隆块所用的 TSDB 索引文件的引用。Gateway 和 Planner 使用这些元数据文件来发现现有块。

每隔 -bloom-build.planner.interval 时间,Planner 将加载所有启用布隆过滤器构建的租户的最新 TSDB 文件,并将这些 TSDB 文件与最新的布隆元数据文件进行比较。如果存在新的 TSDB 文件或其中任何文件发生更改,Planner 将为 TSDB 文件引用的流和块创建一个任务。

Builder 从 Planner 的队列中拉取任务并处理包含的流和块。对于给定的流,Builder 将迭代其新块内的所有日志行,并为该流构建布隆过滤器。如果之前处理过的 TSDB 文件发生更改,Builder 将尝试重用现有块中的布隆过滤器,而不是从头开始构建新的。Builder 转换流的每个块中每行日志的结构化元数据,并将每个键以及键值对的哈希值追加到布隆过滤器中,然后是哈希值与块标识符的组合。第一组哈希值允许 Gateway 跳过整个流,而后者用于跳过单个块。

例如,给定块 c6dj8g 中的结构化元数据 foo=bar,我们将以下哈希值追加到流布隆过滤器中:hash("foo")hash("foo=bar")hash("c6dj8g" + "foo")hash("c6dj8g" + "foo=bar")

查询分片

查询加速不仅发生在处理块时,也发生在查询规划阶段,即查询前端应用查询分片。Loki 3.0 引入了一个新的每个租户配置标志 tsdb_sharding_strategy,默认情况下,它像 Loki 的先前版本一样,使用索引统计信息计算分片,得出最接近 2 的幂,以乐观地将要处理的数据划分为大小大致相同的分片。不幸的是,每个流的数据量通常与其他流不平衡,因此,一些分片最终处理的数据比其他分片多。

查询加速引入了一种新的分片策略:bounded。该策略在查询前端的规划阶段立即使用布隆过滤器减少要处理的块,并均匀分配每个分片查询需要处理的块数量。