ElasticSearch深浅分页查询及原理
假设有8分片,查询到第1000页数据,from =1000 size=100,es每次会从取出每个分片取1000*100+100=11w条数据,自然每个分片都会存储这11w条数据,然后再发给协调节点做排序后,而协调节点就是面临处理8*11w=88w条的巨大压力随着from页码的不断增加,es从每个分片获取的数据量也就越来越大,自然越来越慢,于es所在服务器和应用系统都带来不小压力,甚至出现内存溢出
一、from-size(深分页)
1、分页原理
假设有8分片,查询到第1000页数据,from =1000 size=100,es每次会从取出每个分片取1000*100+100=11w条数据,自然每个分片都会存储这11w条数据,然后再发给协调节点做排序后,而协调节点就是面临处理8*11w=88w条的巨大压力
随着from页码的不断增加,es从每个分片获取的数据量也就越来越大,自然越来越慢,于es所在服务器和应用系统都带来不小压力,甚至出现内存溢出风险。因此es默认使用10000作为最大查询值,超过此值,推荐使用scroll游标来滚动查询。
2、踩坑指南
如果初次使用,不注意的话,当超过10000时候,查询会报如下异常,
Result window is too large, from + size must be less than or equal to: [10000] but was [22020]. 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
当然10000也可以调整,,如最大上限调整为800000
PUT my_index/_settings
{"index.max_result_window":"800000"}
二、滚动查询(浅分页)
1、基本原理
总体而言,scroll查询顾名思义-滚动查询,类似于关系型数据库oracle的cursor游标。初次查询时,将所有符合搜索条件的doc _id集排序后存储在上下文,类似于快照。在之后每次遍历时,带着上次的_scroll_id从这个快照里取size数据,从而从各自_scroll_id对应的分片获取数据。
那么有人说假设一次搜索满足条件的有1000w个doc _id,甚至更多的时候,会不会撑爆内存。这个不用担心,es使用了非常紧凑的数据结构和压缩算法来存放这些ID,占用的内存不会太多。
详细的说,初次查询大致分为两个阶段
Query阶段:将每个shard将命中的结果( doc_id和_score) 按照 _score 顺序在上下文中创建一个优先队列快照,并通过scroll_id指向它,lastEmittedDoc指向上次访问的位置,最后将TOP(size)的doc id返回给协调节点。
Fetch阶段:协调节点将各个shard返回的结果再进行合并排序,最后通过doc_id查找返回结果的全量数据。之后更新各个分片上的上下文。
初次之后查找数据,在Query阶段通过scroll_id找到对应的快照,然后用lastEmittedDoc将原来的查询语句添加bool查询条件**( >=lastEmitted.doc + 1)**,在快照中找数据。
scroll_id也不是固定的,scroll_id其中保留了shard信息,假如scroll查询语句需要路由到16个shard上查。scroll_id会比较长,记录这16个shard。有可能从开始到完成都需要路由到这16个shard,shard_id就不会变化。也有可能随着不断进行scroll,需要路由到的shard越来越少,shard_id也会越来越短,随之变化。
2、操作步骤
(1)初始化scroll缓存
初次请求,要在url中的search后加上scroll=2m,这个scroll=2m(2m代表2分钟),是缓存时间,客户端可以根据查询数据数量自定义缓存的时间
POST my_index/_search?scroll=2m
{
"from": 0,
"size": 150,
"query": {
"bool": {
"must": [
{
"term": {
"bill_month": {
"value": "2022-06",
"boost": 1
}
}
}
]
}
}
}
返回如下,会发现比不带scroll=2m,多返回了一个_scroll_id值为base64的字符串编码
(2)使用
_scroll_id继续请求
用每次得到的这个_scroll_id值,继续请求下一页(这个为了偏于展示,这里_scroll_id值写短些),每次请求最好都带上scroll=2m刷新过期时间,以防超时报错。
POST my_index/_search/scroll?scroll=2m
{
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAMQhbFkhrUXE3eE0tUy1LMkxRMDlhOGU1dEEA"
}
过了缓存时间会抛出如下异常
Elasticsearch exception [type=search_context_missing_exception, reason=No search context found for id [3344636]
(3)清除scroll
这个_scroll_id在es的服务端是有缓存数量限制的,默认最大500,如果请求量大于这个值,会报错。因此除了自然过期之外,我们在处理完成本次请求后一般手动清除掉_scroll_id缓存,及早释放资源
DELETE /_search/scroll
{
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAQ7QAFmtZT3luT3M0UUVDdE82MFp4QmtNcGcAAAAAADOE-hYyMDBxb29mb1J0bWdrS2ZwQ2FhSFZ3AAAAAAAxFi4WSGtRcTd4TS1TLUsyTFEwOWE4ZTV0QQ=="
}
参考:取回阶段 | Elasticsearch: 权威指南 | Elastic
更多推荐
所有评论(0)