在使用ES的过程中肯定绕不开分页查询这个使用场景,在ES中提供了三种分页方案。这三种分页方案没有绝对的好坏之分,在使用时我们需要根据我们的使用场景合理选择使用。
FROM SIZE
这是ES提供的最常见的分页功能了,通过在Search API中指定from
和size
来实现分页效果。例如现在对订单数据做分页:
GET /order_v3/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 10
}
其中from
代表起始位置,size
代表页的大小。这种分页方式在分页深度较浅的情况下没什么问题,但是该方式不适合深度分页。例如将上面的示例中from
的值改成1000000
,那么ES将无法处理抛出以下错误内容。
Result window is too large, from + size must be less than or equal to: [10000] but was [1000010]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.
从错误的内容可以看出,对于深度分页ES是不支持的。虽然可以通过设置index.max_result_window
修改来增加分页深度,但是实际上并不推荐这么做。
那为什么这种方式不适合深度分页呢?例如现在这里的order_v3
索引的primary shard
数量为3,replica shard
数量为0的情况下。如果现在的from
为10000,size
为10,那么这个查询将按照下面的方式进行:
-
客户端请求发送给某一个节点,这里暂且叫它
A
节点。 -
A
节点将请求转发到各个分片(shard),然后各分片按要求获得10010(from+size)条记录,最后将这些记录发送给A
节点。 -
A
节点再将30030(10010*3)条记录再次排序,获得满足条件的10条记录返回给客户端。
从上面可以发现,如果分页深度越深,那么需要从各个shard取的数据量越多,最后合并时节点需要的内存也会越来越多。所以from+size
的这种方式不适合深度分页,只适合页数不深的情况下使用。
Search After
为了应对深度分页,ES推荐使用Search After
的方式来实现数据的深度分页。它的使用方式也很简单,在第一次使用Search API
时附带一个sort参数,其中sort的值必须是唯一的排序值。例如还是对之前的order_v3
索引分页查询,我们按订单创建时间倒序排序查询代码如下:
GET /order_v3/_search
{
"query": {
"match_all": {}
},
"sort": [
{"created.keyword": "desc"},
{"_id":"desc"}
],
"from": 0,
"size": 10
}
我们前面所说的sort的值必须是唯一排序值的意思是,如果我们单纯使用created
即订单创建时间作为排序值,很明显created
不是唯一值。所以我们这里附带加上_id
这样就组成了一个唯一值。
上面的查询结果中,每条记录都会附带一下内容:
{
"sort" : [
"2022-02-27 23:55:39",
"mGA-QIQBiLbZg9L-vfT6"
]
}
如果我们需要获取该查询下一页数据,那么我们使用Search After
可以这么写:
GET /order_v3/_search
{
"query": {
"match_all": {}
},
"sort": [
{"created.keyword": "desc"},
{"_id":"desc"}
],
"size": 10,
"search_after":["2022-02-27 23:55:39","mGA-QIQBiLbZg9L-vfT6"]
}
其中search_after
是一个数组,它的值就是上一次查询最后的一条记录结果中sort
中的值。
通过上面的使用示例可以发现,Search After
查询的本质是使用前一页中的一组排序值来检索匹配的下一页
,但是该方式的实现的限制是使用Search After要求后续的请求返回与第一次查询具有相同的查询条件和排序条件
。
虽然Search After
性能和效率都比之前的from+szie
的方案好,但是它最大的缺点就是不能直接跳页,只能一页一页的往下翻。其实这种使用场景在实际业务中也常有,例如微博或者新闻类APP内容浏览,用户都是一直下滑获取新的数据而不是跳页。
Scroll
前面说到的Search After
虽然能处理深分页,但是每次分页还是需要经过一个查询过程。如果现在有一个场景是让你导出所有数据,使用Search After
需要经过很多次查询才能将所有数据全部导出,次数上去了那么效率也就变低了,那么有没有更好的方式来处理呢?
而ES提供的Scroll
就很适合这种情景,Scroll
就是把查询结果缓存一定时间,例如scroll=3m
就是将查询结果缓存3分钟,而响应结果中会增加scroll_id
返回,下一次查询带上scroll_id
即可再次从缓存结果中获取数据。
例如现在需要将所有订单数据导出,那么使用scroll
首先则是查询出所有数据,示例代码如下:
GET /order_v3/_search?scroll=5m
{
"query": {
"match_all": {}
},
"size": 20
}
其响应结果中将增加_scroll_id
返回:
{
"_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAAa1FmtWSnFHdDl3UkdDcWFHT2dqNnBjWEEAAAAAAAAAORYwR1M3NkloUlM3T0xqY05sNlVfc2tBAAAAAAAAADoWMEdTNzZJaFJTN09MamNObDZVX3NrQQ=="
}
接下来的每次查询都通过如下方式进行:
POST _search/scroll
{
"scroll":"5m",
"scroll_id":"DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAAa1FmtWSnFHdDl3UkdDcWFHT2dqNnBjWEEAAAAAAAAAORYwR1M3NkloUlM3T0xqY05sNlVfc2tBAAAAAAAAADoWMEdTNzZJaFJTN09MamNObDZVX3NrQQ=="
}
这里面的scroll_id
就是第一次查询返回的_scroll_id
。接下来我们一直使用上述的查询条件获取数据直接所有数据全部获取完成。
对于scroll
来说,会返回第一次请求时刻的所有文档,之后文档的改变并不会被查询到。
对于scroll
这种方式,它适合大数据量的导出或者离线数据计算等常见,不适合大并发量的情况。
总结
总体来说,ES深度分页的选择不是绝对的,具体还是要根据使用场景。
from+size
适用于常见的查询,例如需要支持跳页并实时查询的场景。但是查询深度过深时,会有深度分页的问题。
Search After
适合不需要分页的实时滚动查询,像浏览微博一样的使用场景。
scroll
它与Search After
类似,但是它更适合数据导出类的场景。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/72827.html