【问题标题】:Why are Lucene/Elasticsearch prefix queries slower than term queries?为什么 Lucene/Elasticsearch 前缀查询比术语查询慢?
【发布时间】:2021-03-22 06:23:01
【问题描述】:

我最近一直在阅读有关 Lucene 和 Elasticsearch 的文章,似乎以下内容是正确的(如果我错了,请纠正我):

  1. 前缀查询比术语查询慢
  2. 后缀查询 (* ing) 比前缀查询 (ing *) 慢

这似乎是一个奇怪的属性组合。也许我需要扩大我正在考虑的数据结构的范围,但是如果一个段的结构类似于哈希表,我可以很容易地看到 1 将是真的(术语查询将是 O(1) 并且前缀查询将需要完整扫描)但是 2 不是真的(前缀和后缀都需要完整扫描)。如果段像排序数组一样布局,我可以很容易地看到 2 将是真的(可以使用二进制搜索 O(log n) 执行前缀查询,并且后缀需要完全扫描)但是 1 将不再为真(术语和前缀查询都需要二分查找)。

我唯一的另一个想法是,可能存在哈希和排序的某种组合来解释这种行为(例如,哈希到某个分区并在该分区内排序)。但是我的理解是 Elasticsearch 按文档标识符进行分区,但倒排索引键是一个术语。因此,一个术语的查询仍然需要将请求发送到所有分区。

谁能给我一些关于这种行为如何/为什么存在的直觉?

注意:

【问题讨论】:

  • 补充说明为什么我相信 1 是真的。一定是这样,否则为什么人们会为 EdgeNGrams 烦恼。

标签: elasticsearch search solr lucene inverted-index


【解决方案1】:

我对 ES 的具体细节不太熟悉,所以他们可能会做一些不同于普通 Solr 的事情 - 但通常情况并非如此。

前缀匹配将比查找单个术语更昂贵,但它并没有那个更昂贵。可以将其与进行范围搜索进行比较(如果您愿意,可以执行 - field:[aa TO ab) 可以与进行 field:aa* 进行比较(理论上);有效地检索位于该范围内的所有标记,然后解析文档集匹配那些标记。

存在更多匹配标记的事实意味着您不能简单地获取附加到单个标记(匹配术语)的列表并检索这些文档,但您必须检索可能很大的一组匹配标记和然后为此计算文档集。然而,这不是一个非常昂贵的计算,但它比单个匹配更昂贵。可以通过查找索引中匹配标记的开始和结束索引来完成查找,然后检索这两者之间的所有术语并找到匹配的文档 ID 集。

foo* 对具有以下术语的索引的查询:

bar, baz, foo, foobar, spam
          ^----------^

将收集foofoobar 附加的文档列表,将其合并然后检索文档。

较慢并不意味着它是灾难性的或没有以任何方式优化;只是它比已经确定了一组文档的直接匹配更昂贵。但是,您的查询中可能已经有多个词条,因此同样的过程(尽管在层次结构中稍高一些)也会发生在那里。

后缀匹配(您的#2)——即在标记开头匹配通配符——代价高昂,因为通常必须考虑索引中的所有标记。索引具有按字母数字排序的术语,因此当您只想查看字符串的末尾时,您必须考虑每个标记都可以匹配,而不管它在索引中的位置 - 所以您可以获得完整的索引扫描。但是,如果这是您经常看到的用例,您可以使用the reverse wildcard filter。这通过反转字符串并具有以相反顺序匹配术语的标记来工作,因此foo 被索引为oof,而通配符搜索变成了oof*

*ar 对具有以下术语的索引的查询:

bar, baz, foo, foobar, spam
?!   ?    ?    ?!      ?

必须查看每个术语以确定它是否以 ar 结尾。

使用 EdgeNGramFilter(您的评论/#3)的原因是您将尽可能多的所需处理转移到索引时间(执行您知道的查询时间的工作,即使前缀查询不是真的昂贵,它们仍然有成本),另外:通配符查询不支持大多数分析。如此多的人最终会针对一组已被提取或以其他方式处理的标记进行通配符查询,然后当他们的通配符查询未生成匹配时感到惊讶。只有一小部分过滤器可以应用于通配符查询(例如 LowercaseFilter)。这些过滤器称为being "Multi term aware",因为在收集文档之前,该过程最终可以将术语扩展为多个术语。

另一个原因是,使用 EdgeNGramFilter 将为您提供每个前缀的正确频率分数,从而为您提供前缀术语的有效评分。

【讨论】:

  • 这非常有意义,谢谢!我引用的文章 (medium.com/@mourjo_sen/…) 提到“......在生产环境中永远不应该使用类似前缀的查询,因为它们非常慢。原因是 ES 中的标记不能直接前缀。所以Elasticsearch 必须在运行时检查每个标记,以查看它是否以所需的文本开头。”从你所说的来看,这不是真的。您只会分析该范围内的所有标记。对吗?
  • 你不分析令牌;标记是标记器的结果,然后在存储之前通过分析处理。输入文本经过分析和过滤,生成的标记存储在 Lucene 索引中。我对 Elasticsearch 的细节不够熟悉,无法说出文章所引用的内容,但从文章所说的内容来看,它谈到了 match_prefix_queries,这是与带有通配符的前缀查询不同的功能(您可以从它的扩展中看到)最多 25 个值执行分析,这都不是通配符前缀查询会做的)。
  • @MatsLindh 感谢您的回答!但是你知道使用前缀查询对边缘 ngram 分析的字段有什么好处吗?据我了解,它应该加快这个过程,因为我们将在倒排索引中找到我们需要的术语。我对边缘 ngram 进行了实验,发现前缀、匹配和查询字符串查询在我的前缀场景中没有显着差异(请求的前缀是一个单词,不需要分析)。索引相当大,有近 5000 万份中等大小的文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-14
  • 2017-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多