Elasticsearch 查询速度快,但检索 _source 时响应时间慢,即使嵌套字段位于 _source_exclude 中

3v0*_*0k4 4 elasticsearch

我有以下映射

\n\n
{\n  "yellows" : {\n    "aliases" : { },\n    "mappings" : {\n      "yellow" : {\n        "properties" : {\n          "ranges" : {\n            "type" : "nested",\n            "properties" : {\n              "geometry" : {\n                "type" : "geo_shape"\n              },\n              "id" : {\n                "type" : "long"\n              },\n              "other1" : {\n                "type" : "keyword"\n              },\n              "other2" : {\n                "type" : "long"\n              },\n              "other3" : {\n                "type" : "long"\n              }\n            }\n          }\n          ...\n        } \n      }\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

越大,查询就越慢size。例如

\n\n
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"}}]}\'\n# size 50 -> "took":71\n\ncurl 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"}}]}\'\n# size 100 -> "took":1421\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
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"}}]}\'\n# size 0 -> "took":32\n\ncurl 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"}}]}\'\n# _source=false -> "took":167\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
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\n# 2.332.822\n\ncurl 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\n# 38.591.502\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

在我看来,查询并不是瓶颈。事实上,用size=0或用的_source=false响应时间很小。所以我怀疑 \xe2\x80\x99 是范围是嵌套字段的事实,即使请求排除它们(即_source_exclude=ranges),Elastic 也会考虑它们。

\n\n

有没有其他方法可以在不更改映射的情况下使查询更快,或者我应该更改映射以使范围不嵌套?

\n

Nik*_*iev 7

你是对的,查询不是瓶颈。您观察到的是请求的获取阶段的时间不断增长,而搜索保持不变且相当小。

Elasticsearch 大致分两个阶段执行搜索请求:Query 阶段Fetch 阶段

在查询阶段,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"}}]}'
Run Code Online (Sandbox Code Playgroud)

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

_source=false当我们有和时会发生什么size=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"}}]}'
Run Code Online (Sandbox Code Playgroud)

_source=false告诉 Elasticsearch 不要从磁盘获取 JSON。它仅返回文档 ID,在本例中按所需顺序排序。排序也主要在内存中执行

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

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

会发生什么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
Run Code Online (Sandbox Code Playgroud)

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

所发生的情况是,Elasticsearch 最终必须访问磁盘,而且还要通过网络发送大量数据(而不仅仅是几个 100KB)。这就是你的查询速度较慢的原因。1.5 秒传输约 36MB 的传输速度为 24MB/秒(200MBit/秒),这是光纤连接的虚拟极限。

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

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

以下是有关调整搜索速度的一些提示,特别是建议为文件系统缓存提供更多内存。

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

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

最后的考虑因素

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

为了识别查询本身的瓶颈,我建议查看Profile API

希望有帮助!