并发段搜索

使用并发段搜索在查询阶段并行搜索段。并发段搜索改善搜索延迟的场景包括:

  • 发送长时间运行的请求时,例如包含聚合或大范围查询的请求

  • 作为将段强制合并为单个段以提高性能的替代方案

背景

在 UDB-SX 中,每个搜索请求遵循分散-收集协议。协调节点接收搜索请求,评估需要哪些分片来处理此请求,并向每个分片发送分片级搜索请求。接收请求的每个分片使用 Lucene 在本地执行请求并返回结果。协调节点合并从所有分片接收到的响应,并将搜索响应发送回客户端。可选地,如果客户端在响应中请求了任何文档字段或整个文档,协调节点可以在返回最终结果之前执行获取阶段。

并发搜索段

在没有并发段搜索的情况下,Lucene 在查询阶段在每个分片的所有段上顺序执行请求。查询阶段然后收集搜索请求的顶部命中结果。使用并发段搜索,每个分片级请求将在查询阶段并行搜索段。对于每个分片,段被划分为多个切片。每个切片是可以在一个单独的线程上并行执行的工作单元,因此切片数量决定了分片级请求的最大并行度。一旦所有切片完成其工作,Lucene 对切片执行归约操作,合并它们并为该分片级请求创建最终结果。切片使用新的 index_searcher 线程池执行,该线程池与处理分片级请求的 search 线程池不同。

在索引或集群级别启用并发段搜索

您可以使用 search.concurrent_segment_search.mode 设置来配置集群上的并发段搜索。现有的 search.concurrent_segment_search.enabled 设置将在未来版本发布中被弃用,转而采用新设置。

默认情况下,并发段搜索在集群上被禁用。您可以在两个级别启用并发段搜索:

  • 集群级别

  • 索引级别

索引级别的设置优先于集群级别的设置。因此,如果集群设置已启用但索引设置被禁用,则并发段搜索将对该索引禁用。因此,除非显式设置,否则不会评估索引级别的设置,而无论该设置的默认值如何配置。您可以通过调用索引设置 API 并省略 ?include_defaults 查询参数来检索索引级别设置的当前值。

集群和索引级别的 search.concurrent_segment_search.mode 设置都接受以下值:

  • all:在所有搜索请求中启用并发段搜索。这相当于将 search.concurrent_segment_search.enabled 设置为 true

  • none:对所有搜索请求禁用并发段搜索,有效关闭该功能。这相当于将 search.concurrent_segment_search.enabled 设置为 false。这是默认行为。

  • auto:在此模式下,UDB-SX 将使用可插拔的并发搜索决策器,根据查询评估和请求中是否存在聚合来决定对搜索请求使用并发路径还是顺序路径。默认情况下,如果任何插件没有配置决策器,则将基于请求中是否存在聚合来决定是否使用并发搜索。有关可插拔决策器语义的更多信息,请参阅可插拔并发搜索决策器

要为集群中每个索引的所有搜索请求启用并发段搜索,请发送以下请求:

PUT _cluster/settings
{
   "persistent":{
      "search.concurrent_segment_search.mode": "all"
   }
}

要为特定索引的所有搜索请求启用并发段搜索,请在端点中指定索引名称:

PUT <index-name>/_settings
{
    "index.search.concurrent_segment_search.mode": "all"
}

您可以继续使用现有的 search.concurrent_segment_search.enabled 设置来为集群中的所有索引启用并发段搜索,如下所示:

PUT _cluster/settings
{
   "persistent":{
      "search.concurrent_segment_search.enabled": true
   }
}

要为特定索引启用并发段搜索,请在端点中指定索引名称:

PUT <index-name>/_settings
{
    "index.search.concurrent_segment_search.enabled": true
}

在评估集群上是否启用并发段搜索时,search.concurrent_segment_search.mode 设置优先于 search.concurrent_segment_search.enabled 设置。 如果未显式设置 search.concurrent_segment_search.mode 设置,则将评估 search.concurrent_segment_search.enabled 设置以决定是否启用并发段搜索。

当从早期版本升级集群,并且该版本指定了旧的 search.concurrent_segment_search.enabled 设置时,该设置将继续有效。但是,一旦设置了 search.concurrent_segment_search.mode,它将覆盖先前的设置,根据指定的模式启用或禁用并发搜索。 我们建议在配置 search.concurrent_segment_search.mode 后,将集群上的 search.concurrent_segment_search.enabled 设置为 null

PUT _cluster/settings
{
   "persistent":{
      "search.concurrent_segment_search.enabled": null
   }
}

要为特定索引禁用旧设置,请在端点中指定索引名称:

PUT <index-name>/_settings
{
    "index.search.concurrent_segment_search.enabled": null
}

切片机制

您可以在两种可用的机制中选择一种来将段分配给切片:默认的 Lucene 机制最大切片计数机制

Lucene 机制

默认情况下,Lucene 将最多 25 万份文档或 5 个段(以先达到者为准)分配给分片中的每个切片。例如,考虑一个有 11 个段的分片。前 5 个段每个有 25 万份文档,后 6 个段每个有 2 万份文档。前 5 个段将各自分配给 1 个切片,因为它们各自包含切片允许的最大文档数。然后,后 5 个段将全部分配给另一个单独的切片,因为每个切片允许的最大段数。第 11 个段将被分配给一个单独的切片。

最大切片计数机制

最大切片计数机制是一种替代切片机制,它使用动态可配置的最大切片数,并以轮询方式将段分配给切片。当已经存在太多顶级分片请求,并且您希望限制每个请求的切片数量以减少切片之间的竞争时,这很有用。

设置切片机制

默认情况下,并发段搜索使用 Lucene 机制计算每个分片级请求的切片数量。 要改为使用最大切片计数机制,您可以在集群级别或索引级别设置并发段搜索的切片数量。

要为集群中的所有索引配置切片数量,请使用以下动态集群设置:

PUT _cluster/settings
{
   "persistent":{
      "search.concurrent.max_slice_count": 2
   }
}

要为特定索引配置切片数量,请在端点中指定索引名称:

PUT <index-name>/_settings
{
    "index.search.concurrent.max_slice_count": 2
}

集群和索引级别的 search.concurrent.max_slice_count 设置都可以接受以下有效值:

  • 0:使用默认的 Lucene 机制。

  • 正整数:使用最大目标切片计数机制。通常,2 到 8 之间的值应该足够。

通用指南

并发段搜索有助于提高搜索请求的性能,但代价是消耗更多资源,例如 CPU 或 JVM 堆。测试您的工作负载以了解集群是否针对并发段搜索正确调整非常重要。我们建议遵循以下并发段搜索指南:

  • 从切片数量为 2 开始,并测量工作负载的性能。如果资源利用率超过推荐值,则考虑扩展您的集群。根据我们的测试,我们观察到如果工作负载已经消耗了超过 50% 的 CPU 资源,则需要为并发段搜索扩展集群。

  • 如果切片数量为 2 并且集群中仍有可用资源,则可以将切片数量增加到更高的数字,例如 4 或 6,同时监控搜索延迟和集群中的资源利用率。

  • 当许多客户端并行发送搜索请求时,较低的切片数量通常效果更好。这反映在 CPU 利用率上,因为更多的客户端导致每秒更多的查询,这转化为更高的资源使用率。

限制

以下聚合不支持并发搜索模型。如果搜索请求包含以下聚合之一,即使集群级别或索引级别启用了并发段搜索,请求也将使用非并发路径执行。

  • 基于连接字段的父聚合。有关更多信息,请参阅此 GitHub 问题(文档联系售前工作人员获取)。

  • samplerdiversified_sampler 聚合。有关更多信息,请参阅此 GitHub 问题(文档联系售前工作人员获取)。

其他注意事项

以下部分提供了并发段搜索的其他注意事项。

terminate_after 搜索参数

terminate_after 搜索参数 用于在收集到指定数量的文档后终止搜索请求。如果在请求中包含 terminate_after 参数,并发段搜索将被禁用,请求将以非并发方式运行。

通常,查询与较小的 terminate_after 值一起使用,因此完成得很快,因为搜索是在减少的数据集上执行的。因此,在这种情况下,并发搜索可能不会进一步改善性能。此外,当 terminate_after 与其他搜索请求参数(如 track_total_hitssize)一起使用时,会增加复杂性并改变预期的查询行为。对包含 terminate_after 的搜索请求回退到非并发路径可确保并发和非并发请求之间结果一致。

排序

根据段的数据布局,排序优化功能可以根据最小值和最大值以及先前收集的值来剪枝整个段。如果顶部值存在于前几个段中,并且所有其他段都被剪枝,则在使用并发段搜索进行排序时,查询延迟可能会增加。相反,如果后几个段包含顶部值,则并发段搜索可能会改善延迟。

词条聚合

非并发搜索计算文档计数错误并在 doc_count_error_upper_bound 响应参数中返回。在并发段搜索期间,shard_size 参数在段切片级别应用。因此,并发搜索可能会引入额外的文档计数错误。

有关 shard_size 如何影响 doc_count_error_upper_bound 和收集的桶的更多信息,请参阅此 GitHub 问题(文档联系售前工作人员获取)。

开发者信息

以下部分为开发者提供了更多信息。

AggregatorFactory 更改

由于实现细节,并非所有聚合器类型都支持并发段搜索。为此,我们在 AggregatorFactory 类中引入了 supportsConcurrentSegmentSearch()(文档联系售前工作人员获取) 方法,以指示给定的聚合类型是否支持并发段搜索。默认情况下,此方法返回 false。任何需要支持并发段搜索的聚合器必须在其自身的工厂实现中覆盖此方法。

为确保基于插件的自定义 Aggregator 实现在并发搜索路径中正常工作,插件开发者可以在启用并发搜索的情况下验证其实现,然后更新插件以覆盖 supportsConcurrentSegmentSearch()(文档联系售前工作人员获取) 方法,使其返回 true

可插拔并发搜索决策器:ConcurrentSearchRequestDecider

插件开发者可以通过扩展ConcurrentSearchRequestDecider (文档联系售前工作人员获取) 并通过 SearchPlugin#getConcurrentSearchRequestFactories()(文档联系售前工作人员获取) 注册其工厂来自定义 auto 模式的并发搜索决策。决策器仅在请求不属于限制其他注意事项部分列出的任何类别时被评估。有关决策器实现的更多信息,请参阅相应的 GitHub 问题(文档联系售前工作人员获取)。 搜索请求使用 QueryBuilderVisitor 进行解析,该访问器为搜索请求的 QueryBuilder 树的每个节点调用所有配置的决策器的 ConcurrentSearchRequestDecider#evaluateForQuery()(文档联系售前工作人员获取) 方法。最终的并发搜索决策通过组合每个决策器的 ConcurrentSearchRequestDecider#getConcurrentSearchDecision()(文档联系售前工作人员获取) 方法返回的决策来获得。