Elasticsearch快速查询但在检索_source时响应时间很慢,即使嵌套字段位于_source_exclude中也是如此

时间:2018-06-18 10:04:42

标签: elasticsearch

我有以下映射

{
  "yellows" : {
    "aliases" : { },
    "mappings" : {
      "yellow" : {
        "properties" : {
          "ranges" : {
            "type" : "nested",
            "properties" : {
              "geometry" : {
                "type" : "geo_shape"
              },
              "id" : {
                "type" : "long"
              },
              "other1" : {
                "type" : "keyword"
              },
              "other2" : {
                "type" : "long"
              },
              "other3" : {
                "type" : "long"
              }
            }
          }
          ...
        } 
      }
    }
  }
}

查询越慢越size。例如

curl https://path/to/elastic/yellows/_search?_source_exclude=ranges&from=0&size=50' --data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}'
# size 50 -> "took":71

curl https://path/to/elastic/yellows/_search?_source_exclude=ranges&from=0&size=100' --data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}'
# size 100 -> "took":1421

与此同时,size=0_source=false的查询速度很快。例如

curl https://path/to/elastic/yellows/_search?_source_exclude=ranges&from=0&size=0' --data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}'
# size 0 -> "took":32

curl https://path/to/elastic/yellows/_search?_source=false&from=0&size=100' --data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}'
# _source=false -> "took":167

这意味着检索_source s(即没有_souce=falsesize=0)的查询速度较慢。此外,似乎检索到的文档中的范围越多,响应越慢。我在下面使用wc -c作为检索文档中有多少范围的代理度量。不是最好的措施,但应该足够

curl https://path/to/elastic/yellows/_search?from=0&size=50' --data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}' | wc -c
# 2.332.822

curl https://path/to/elastic/yellows/_search?from=50&size=50' --data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}' | wc -c
# 38.591.502

正如你所看到的,前50个的范围远远小于前100个中的第50个。另外,请注意,在第一个片段中,前50个的查询比第二个50的查询要快得多,即使它有_source_exclude=ranges

在我看来,查询不是瓶颈。实际上,使用size=0_source=false时,响应时间很短。因此,我怀疑范围是一个嵌套字段,即使请求排除它们,Elastic也会考虑它们(即_source_exclude=ranges)。

有没有其他方法可以在不更改映射的情况下加快查询速度,还是应该更改映射以使范围不嵌套?

1 个答案:

答案 0 :(得分:1)

您是对的,查询不是瓶颈。您看到的是请求fetch phase的增长时间,而搜索保持不变并且很小。

Elasticsearch大致在两个阶段执行搜索请求:Query phaseFetch phase

在查询阶段,ES会确定哪些文档与查询匹配,并为此使用快速索引,这些索引很可能缓存在RAM中。

在获取阶段,它实际上是从磁盘中获取它们并发送回响应。 (由于匹配的文档可以位于群集的任何节点中,所以实际上是分布式的,因为获取的文档可以位于该群集的任何节点。)

现在让我们看看在您提到的每种情况下会发生什么。

当我们拥有size=0时会发生什么?

curl https://path/to/elastic/yellows/_search?_source_exclude=ranges&from=0&size=0' \
--data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}'

在这种情况下,您告诉Elasticsearch跳过获取阶段:它仅返回匹配的文档数。它也不做任何排序,因为它是不需要的。

当我们拥有_source=falsesize=100时会发生什么?

curl https://path/to/elastic/yellows/_search?_source=false&from=0&size=100' \
--data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}'

_source=false告诉Elasticsearch不要从磁盘获取JSON。它仅返回文档ID,在这种情况下按所需顺序排序。排序也是performed mostly in memory

  

排序时,将相关的排序字段值加载到内存中。   这意味着每个分片应该有足够的内存来包含   他们。

这就是为什么这个查询也很快的原因。

from=50&size=50会发生什么?

curl https://path/to/elastic/yellows/_search?from=50&size=50' \
--data-binary '{"query":{"bool":{"must":[],"filter":{"bool":{"filter":[{"terms":{"...":["1"]}},{"terms":{"...":["..."]}}],"should":[]}}}},"sort":[{"...":{"order":"asc"}}]}' | wc -c

在这里,我们要求Elasticsearch跳过前50条记录,再给出下50条记录。正如您所测量的,响应约为36MB,这是很大的。前50条记录仅需要传输2MB数据。

发生的事情是,Elasticsearch最终必须访问磁盘,并且还要通过网络发送大量数据(不仅是100KB的数据)。这就是为什么您的查询速度较慢的原因。 1.5秒的传输时间〜36MB使我们的传输速度达到24MB /秒(200 MBit /秒),例如,这是光纤连接的虚拟极限。

确实,查询嵌套字段的速度比普通字段慢,但是在这种情况下,这不太可能成为问题:从磁盘读取数据并通过网络发送数据。

如何改善与磁盘相关的瓶颈?

Tuning for search speed上有一些提示,特别是建议给文件系统缓存更多的内存。

如何通过网络发送更少的数据?

您已经发现可以使用_source_exclude=ranges从响应中排除某些数据。如果您仍然需要响应,但只关心该ranges数组的子集,则可以使用inner_hits这样做。默认情况下,它将返回前3个匹配的嵌套子文档。

最终注意事项

总是很难给出性能优化建议,因为它很大程度上取决于数据结构,数量和用例。识别瓶颈很重要;在您的情况下,我将首先检查磁盘的读写功能和网络。

要确定查询本身的瓶颈,我建议看一下Profile API

希望有帮助!