原文链接:你还不会ElasticsSearch分页查询?那你看这一篇就够了,快拿走吧
引言
我们使用mysql的时候经常遇到分页查询的场景,在mysql中使用limit关键字来实现分页。比如下面的示例
select * from orders where category='man' limit 100,100;
ElasticsSearch 同样也有很多分页查询的场景,特别是在数据量比较大,而且查询条件比较复杂的情况下。在 mysql 中如果在某种无法命中索引的情况下,可以选择使用ES的分页查询进行功能替换。
ES实现分页查询有几种不同的方案,每种方案都有自己的优缺点,下面就带你领略一下 ES 分页查询的魅力吧。
1、from/size方案
ES 分页最常用的一种方案,类似 mysql 里的limit,from指定查询的起始位置,size表示从起始位置开始的文档数量,如下基础查询:
GET kibana_sample_data_flights/_search
其中:
- from:未指定,默认值是 0,注意不是1,代表当前页返回数据的起始值。
- size:未指定,默认值是 10,代表当前页返回数据的条数。
- 默认返回前10个匹配的匹配项。
优缺点
- 优点:支持随机翻页,
- 缺点:受制于 max_result_window 设置,不能无限制翻页,越往后翻页越慢,存在深度翻页问题。
适用场景
- 非常适合小型数据集或者大数据集返回 Top N结果集的业务场景
- 支持随机跳转分页的业务场景
2、search_after 查询
有时候我们会遇到一些业务场景,需要进行很深度的分页,但是可以不指定页数翻页,只要可以实时请求下一页就行。比如一些无限下拉的瀑布流页面。
search_after 查询方案是使用前一页中的一组排序值来检索匹配的下一页。利用实时有游标来帮我们解决实时滚动的问题,简单来说前一次查询的结果会返回一个唯一的字符串,下次查询带上这个字符串,进行下一页的查询。
类似下面这个查询:
GET /_search
{
"size" : 2,
"sort": [
{
"order_datetime": "desc",
"_id": "asc"
}
]
}
查询结果的最后,有个sort的json结构:
"sort": [
1704147780000,
"um1362393"
]
这里记录了上一次查询的位置,所以当在查询下一页时,只要把这些值带上就可以实现。
GET /_search
{
"size" : 2,
"sort": [
{
"order_datetime": "desc",
"_id": "asc"
}
],
"search_after": [
1704147780000,
"um1362393"
]
}
就这样一直操作就可以实现不断的查看下一页了。很显然,这个查询操作的开销会很小,但是缺点就是无法进行随机翻页,可以类比链表和数组。
3、Scroll 遍历查询
相比于 from/size 和 search_after 返回一页数据,Scroll API 可用于从单个搜索请求中检索大量结果。但是 scroll 滚动遍历查询是非实时的,数据量大的时候,响应时间可能会比较长。
示例如下:
POST /_search?scroll=3m
{
"size": 100,
"query": {
"match": {
"host": "elastic"
}
}
}
首先第一次查询后,会生成一个当前查询条件结果的快照,后面的每次叫翻页都是基于这个快照的结果,也就是即使有新的数据进来也不会被查询到。
上面这个查询结果会返回一个scroll_id,拷贝过来,组成下一条查询语句
POST /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFlNpZU5wVVhoUlhHd0NqNEhsNktKQmcAAAAACz8y-xZ3SFBodGo1MVJNdV9UN21jMmo5aGd3"
}
以此类推,后面每次滚屏都把前一个的scroll_id
复制过来。注意到,后续请求时没有了index信息,size信息等,这些都在初始请求中,只需要使用scroll_id和scroll两个参数即可。
总结
- from/size方案的优点是简单,缺点是在深度分页的场景下系统开销比较大。
- search after 可以实时高效的进行分页查询,但是只能做下一页的查询场景,不能进行随机页数查询。
- scroll方案基于快照,不能用在高实时性的场景下,建议用在类似数据导出场景下使用。