【问题标题】:Elasticsearch: Order of filters for best performanceElasticsearch:过滤器的顺序以获得最佳性能
【发布时间】:2016-01-11 17:07:11
【问题描述】:

Elasticsearch 指南说

“每个过滤器都是独立计算和缓存的,不管它在哪里使用。如果两个不同的查询使用相同的过滤器,相同的过滤器位集将被重用。同样,如果单个查询在多个地方使用相同的过滤器,只计算一个位集,然后重用。” (https://www.elastic.co/guide/en/elasticsearch/guide/current/filter-caching.html)

在另一个页面上还写着:

“bool 子句中过滤器的顺序对性能很重要。更具体的过滤器应该放在不太具体的过滤器之前,以便尽早排除尽可能多的文档。如果子句 A 可以匹配 10百万个文件,而条款 B 只能匹配 100 个文件,则应将条款 B 放在条款 A 之前。” (https://www.elastic.co/guide/en/elasticsearch/guide/current/_filter_order.html)

当每个过滤器独立缓存时,我不太明白 bool 子句中过滤器的顺序有多重要。

我想从缓存中执行或检索子句 B,从缓存中执行或检索子句 A,然后“合并”过滤器位集。为什么顺序很重要?

【问题讨论】:

    标签: performance elasticsearch


    【解决方案1】:

    这个指导有点误导。它更复杂,很难尝试编写一套适合所有情况的规则。随着数据的变化,规则也会发生变化。随着查询和过滤器类型的改变,规则也会改变。特定过滤器的执行速度可能比广泛的过滤器慢,规则会发生变化。在每个段的基础上,过滤器的结果大小可能与另一个段相反,它并不总是可预测的。 所以首先你必须了解更多的内部结构,然后你需要在进入现代 Elasticsearch 2.x 时放弃试图控制它。

    注意: 您的第二个报价(过滤顺序)和相关链接指向的页面对于 Elasticsearch 2.x 而言被视为“过时”,稍后会更新。因此,这些建议可能适用于现代,也可能不适用于现代。

    及时回顾 Elasticsearch 1.x 以及订购建议的原因:

    让我们先谈谈过滤器在内存中是如何表示的。它们要么是匹配文档的迭代列表,要么是随机访问“它在这里”模型。取决于过滤器的类型,取决于哪个更有效。现在,如果所有内容都被缓存,那么您只是将它们相交,并且成本会因大小和类型而异。

    如果过滤器未缓存,但可缓存,则过滤器将独立执行,之前的过滤器仅会通过交集的总成本影响它。

    如果过滤器不可缓存,那么它可以由以前的结果指导。想象一个Query 加上一个Filter。如果您执行查询,并在应用过滤器之后,如果过滤器限制为非常小的记录集,您将做很多额外的工作。您在收集、评分和整体构建大量结果方面浪费了时间。但是,如果您转换为FilteredQuery 并同时执行这两项操作,那么Query 将忽略所有已被Filter 消除的记录。它只需要考虑已经在使用的相同文件。这称为“跳过”。并非所有过滤器类型都利用跳过,但有些可以。这就是为什么较小的“引导”过滤器会使其他人更快地使用它。

    除非您了解每种过滤器类型、数据的启发式方法以及每个特定过滤器将如何受到这些过滤器的影响,否则您没有足够的信息来说明“将最受限制的过滤器放在首位,和更大的第二个”,希望它能成功。对于bool,默认情况下不缓存其整体结果,因此您必须注意其重复性能(和/或缓存它)。当过滤器交叉点的一侧较小时,效率更高。因此,从一个小的开始会使所有其他交叉口更快,因为它们只能变得更小。如果是 bool query 而不是 filter 进行评分,那么避免对更多文档进行评分就更重要了。

    另一个重要的注意事项是“最具体的过滤器优先”有时可能会很慢(脚本过滤器或其他),所以它应该真正阅读:“最低成本,最具体的过滤器第一个”

    Elasticsearch 2.0, things will change:

    是时候忘记您对查询和过滤器的所有了解了:Elasticsearch 2.0 将自行做出更好的决策,而不是依赖用户来制定优化的查询。

    在 2.x 中,您应该减少对系统的博弈,让引擎做出最佳选择。引擎实际上可能最终会在引擎盖下产生完全不同的东西,重写过滤器,内部结构和数据的彻底改变。而且您甚至可能不再控制缓存。因此,您需要阅读更多相关信息。

    之前的过滤器 API 可以通过两种方式使用:或者使用匹配文档的迭代器,或者使用允许检查特定文档是否匹配过滤器的可选随机访问 API。到目前为止一切都很好,除了使用过滤器的最佳方式取决于您拥有哪种过滤器:例如,script 过滤器在使用随机访问 API 时更有效,而bool 过滤器更有效使用迭代器 API。这对优化来说简直是一场噩梦,这也是为什么一方面 bool 过滤器与另一方面 andor 过滤器表现不同的根本原因。

    引擎现在将考虑更多因素来决定什么是最好的,包括评分、结果大小的估计、与相关过滤器相交的最佳方法,甚至可能基于每个段等等。

    此外,这篇文章清楚地表明,即使缓存也可能会产生误导,它并不总是能让事情变得更快。有时内部数据结构在最初使用时比始终缓存的位集结构更好。因此,在 2.x 中,这也在发生变化,以避免缓存从本机数据结构中执行得更好的东西,而无需缓存。

    在博文Roaring Bitmaps中有更多详细信息:

    显然,最重要的要求是快速:如果缓存的过滤器比再次执行过滤器慢,它不仅会消耗内存,还会使查询变慢。编码越复杂,就越有可能因为 CPU 使用率增加而减慢编码和解码速度

    在这里,您可以获得大量关于内部数据结构、缓存、交集等 2.x 内部变化的信息,这将帮助您更深入地了解过滤器性能。

    如果您不熟悉搜索引擎内部结构,您可能会感到惊讶,但搜索引擎最重要的组成部分之一是能够有效地压缩和快速解码已排序的整数列表。

    从最近的几个 2.x 博客链接中,您对您的问题有很多背景知识,它们讨论了您尝试使用过滤器排序解决的所有问题。信息和细节都在那里,您可以更好地了解 1.x 与 2.x 以及如何解决查询+过滤器。所以请记住:

    没有任何特定的实现总是比所有其他的都好。

    另请参阅这些 1.x 资源以获取历史参考:

    • Optimizing Elasticsearch searches 涵盖了更多关于过滤器排序的内容。概括地说:

      也就是说,您仍然需要考虑过滤的顺序。您希望首先运行选择性更强的过滤器。假设您过滤类型:书和标签:elasticsearch。如果你有 3000 万个文档,1000 万个类型的书,并且只有 10 个标记的 Elasticsearch,你会想要首先应用标记过滤器。它比书籍过滤器减少的文档数量更多。

    • All About Elasticsearch Filter Bitsets 被认为是现代已过时的文章,但它提供了有关您引用的过滤器订购文档的更多背景信息。

    • A forum answer by Martijn v Groningen 似乎与 booland 查询使用迭代与随机访问的情况相反,但两者的想法是相同的:通过在早期限制文档来确保安全过滤器列表——无论哪种模型用于一种类型与另一种类型。

    【讨论】:

    • Jayson,感谢您花时间回答这个问题
    【解决方案2】:

    这个blog post on elastic website 发布于 2017 年 5 月说

    问:我在查询 DSL 中放置查询/过滤器的顺序是什么? 重要吗?

    A:不,因为无论如何它们都会自动重新排序 基于各自的成本和匹配成本。

    【讨论】:

      【解决方案3】:

      并非所有过滤器都被缓存/可缓存。例如,使用now 变量的日期范围过滤器不会被缓存,因为它一直在变化。如果您在您提供的第一个链接中再往下看,您会看到一个名为 "Controlling caching" 的部分,其中说明了这一事实:

      但是,默认情况下不缓存某些叶过滤器,因为这样做没有意义:脚本过滤器、地理过滤器、日期范围过滤器。

      为了说明这一点,假设我们有以下日期 range 过滤器(我们称之为过滤器 A),它过滤过去一个月的所有文档

      "range" : {
          "timestamp" : {
               "gt" : "now-1m"
          }
      }
      

      还有一个term过滤器(我们称之为过滤器B)来过滤XYZ类型的文档

      "term" : {
          "type" : "XYZ"
      }
      

      如果你放置

      ,它会产生很大的不同(性能方面)
      1. 在过滤器 B 之前过滤 A 或
      2. 先过滤 B,再过滤 A

      在第一种情况下,执行会比较慢,因为过去一个月的所有文档都需要先经过过滤器A,它没有被缓存。

      在情况 2 中,您首先过滤掉所有没有 type XYZ 的文档,因为过滤器 B 被缓存,所以速度很快。然后通过过滤器 B 的文档可以通过过滤器 A。所以即使过滤器 A 没有被缓存,执行仍然会更快,因为过滤器管道中剩下的文档要少得多。

      这是一个非常简单的例子,但它应该说明为什么过滤器顺序很重要,即主要是因为某些过滤器没有被缓存。您可以通过强制缓存来更改默认行为,但有时这不是一个好主意。最佳实践是首先应用最激进的过滤器,以便让尽可能少的文档通过下一个过滤器。

      我个人将其称为“推土机方法”,即首先确保在过滤器管道中尽可能早地处理尽可能多的材料,最终您会得到更易咀嚼的数据块,可以进行大量处理更快。

      【讨论】:

      • 您好 Val,首先感谢您的详尽回答。我知道你描述的场景。对我来说,这基本上是elastic.co/guide/en/elasticsearch/guide/current/… 的第二部分,开头是“缓存的过滤器非常快,所以它们应该放在不可缓存的过滤器之前。”但是这个页面的第一部分说“更具体的过滤器应该放在不太具体的过滤器之前,以便尽早排除尽可能多的文档。”
      • 它没有说“应将可缓存过滤器放在不可缓存过滤器之前,以便尽早排除尽可能多的文档。”它甚至给出了两个过滤器的例子,每个过滤器都有固定数量的文档——所以它们似乎都是可缓存的。 “如果条款 A 可以匹配 1000 万个文档,而条款 B 只能匹配 100 个文档,那么条款 B 应该放在条款 A 之前。”如果这些过滤器中的每一个都是独立计算和缓存的,那么我不明白为什么顺序很重要。
      • 但也许我读的太多了,他们真的只想告诉我们把可缓存的过滤器放在不可缓存的过滤器之前。
      • 还要注意,从 ES 2.0 开始,关于查询与过滤器的所有内容现在都被颠倒了,因为查询和过滤器 have been merged 和 ES 现在足够聪明,可以判断是否执行某些操作在查询或过滤上下文中和/或是否to cache filters at all。所以我不会过多地阅读它,因为它不像以前那样棘手。
      • 感谢 Val,感谢您的反馈。
      【解决方案4】:

      我想要么将较小的匹配文档集合并到较大的集合中更有效,要么按此顺序操作会提供更高的零匹配机会,因此可以采用捷径。需要检查源代码(ElasticsearchLucene)才能确定。

      如果我错了,请纠正我......

      【讨论】:

        猜你喜欢
        • 2011-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-07
        • 2022-01-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多