写点什么

Elasticsearch 分页搜索以及 deep paging 性能问题

用户头像
escray
关注
发布于: 2021 年 02 月 07 日
Elasticsearch 分页搜索以及 deep paging 性能问题

Elasticsearch 分页搜索以及 deep paging 性能问题,文字内容来自 B 站中华石杉 Elasticsearch 高手进阶课程,英文内容来自官方文档。

如何使用 Elasticsearch 进行分页搜索


By default, searches return the top 10 matching hits. To page through a larger set of results, you can use the search API's from and size parameters. The from parameter defines the number of hits to skip, defaulting to 0. The size parameter is the maximum number of hits to return. Together, these two parameters define a page of results.


GET /_search?size=10GET /_search?size=10&from=0GET /_search?size=10&from=20
复制代码


分页的上机实验


GET /test_index/test_type/_search
"hits": { "total": 9, "max_score": 1,
复制代码


我们假设将这 9 条数据分成 3 页,每一页是 3 条数据,来实验一下这个分页搜索的效果


GET /test_index/_search?from=0&size=3GET /test_index/_search?from=3&size=3GET /test_index/_search?from=6&size=3
复制代码


我在这里一直有一个误解,我曾经以为 from 的参数传递的是页码,后来试验了一下,才发现传递的是序号。

deep paging 性能问题


什么是 deep paging 问题?为什么会产生这个问题,它的底层原理是什么?


Avoid using from and size to page too deeply or request too many results at once. Search requests usually span multiple shards. Each shard must load its requested hits and the hits for any previous pages into memory. For deep pages or large sets of results, these operations can significantly increase memory and CPU usage, resulting in degraded performance or node failures.


By default, you cannot use from and size to page through more than 10,000 hits. This limit is a safeguard set by the index.max_result_window index setting. If you need to page through more than 10,000 hits, use the search_after parameter instead.


Deep Paging 的意思就是搜索的特别深,比如做能够有 60000 条数据,每个 Shard 上分了 20000 条,每页 10 条数据,这个时候,如果你要搜索到第 1000 页,实际上要拿到的是 10001~10010 条数据,那么每个 Shard 如何返回数据?


每个 Shard 都要返回 10000 条之后的 10 条?不是这样理解的


你的请求首先可能到达一个不包含这个 index 的 Shard 的节点上,这个节点 Node 就成为一个 Coordinating Node,那么这个 Coordinating Node 就会将搜索请求转发到 Index 的三个 Shard 所在的 Node 上。


比如刚才的搜索,要搜索 60000 条数据中的第 1000 页,实际上每个 Shard 都要将其内部的 20000 条数据中的第 1~10010 条数据拿出来,注意不是 10 条(第 10001~10010 条),而是 10010 条。这样一来,3 个 Shard 每个 Shard 都返回 10010 条数据给 Coordinating Node,Coordinating Node 会收到总共 30030 条数据,然后在这些数据中排序,按照相关度分数 _score,最后取第 1000 页,也就是排序从 10000 到 10010 的数据,其实就是我们要的第 1000 页的 10 条数据。


这里应该有办法可以做一些算法上的优化,类似从三个数组取一个有序队列,只要前 10010 条数据,但是仍然是比较耗费资源的。


当搜索过深的时候,就需要在 Coordinating Node 上保存大量数据,还需要进行排序,排序之后再取出对应的那一页。这种情况下,既耗费网络带宽,耗费内存,也耗费 CPU,所以 Deep Paging 会有比较严重的性能问题。在业务开发中,一般会尽力避免 Deep Paging 问题。



其实所有的论坛、搜索引擎都尽量避免类似的 Deep Paging 问题。


按照一般的经验,一般情况下在使用搜索引擎或者分页排序的时候,不会去点击特别靠后的页面。如果在检索的情况下得到的数据条目数特别多,其实更合理的处理方法是修改查询关键字,或者过滤条件,让结果尽可能少一点。


为了应对这个问题,其实 Elasticsearch 还有一个 search_after 的参数,不过这个属于高级主题,以后再说。


最早看到关于这个问题的描述,是从曹政那里。

Search after


You can use the search_after parameter to retrieve the next page of hits using a set of sort values from the previous page.


Using search_after requires multiple search requests with the same query and sort values. If a refresh occurs between these requests, the order of your results may change, causing inconsistent results across pages. To prevent this, you can create a point in time (PIT) to preserve the current index state over your searches.



另外似乎还有一个 Scroll search results 的方法,但是似乎官方已经不推荐了。


We no longer recommend using the scroll API for deep pagination. If you need to preserve the index state while paging through more than 10,000 hits, use the search_after parameter with a point in time (PIT).


在学习中华石杉大大课程的时候,已经感觉到其实 3 年左右的时间里面,Elasticsearch 的变化还是很大的,而我相信,国内很多公司可能还是在用老版本的 Elasticsearch,欢迎打脸。


之前发现很多公司在用 Rails 4.2 版本(大多是 2015 年左右的创业公司)。


发布于: 2021 年 02 月 07 日阅读数: 26
用户头像

escray

关注

Let's Go 2017.11.19 加入

在学 Elasticsearch 的项目经理

评论

发布
暂无评论
Elasticsearch 分页搜索以及 deep paging 性能问题