分页查询执行原理
POST /my_index/my_type/_search{"query": { "match_all": {}},"from": 100,"size": 10}
- Client 发送一次搜索请求,node1 接收到请求,然后,node1 创建一个大小为from + size的优先级队列用来存结果,我们管 node1 叫 coordinating node。
- coordinating node将请求广播到涉及到的 shards,每个 shard 在内部执行搜索请求,然后,将结果存到内部的大小同样为from + size 的优先级队列里,可以把优先级队列理解为一个包含top N结果的列表。
- 每个 shard 把暂存在自身优先级队列里的数据返回给 coordinating node,coordinating node 拿到各个 shards 返回的结果后对结果进行一次合并,产生一个全局的优先级队列,存到自身的优先级队列里。
Fetch阶段
- 取出对应区间的_id,coordinating node 发送 GET 请求到相关shards
- shard 根据 doc 的_id取到数据详情,然后返回给 coordinating node
- coordinating node 返回数据给 Client。
存在问题
- 随着分页深度的增加,查询成本越来越高,性能越来越差
- 这种请求并不合理,因为通常只需要前几条数据,很多查询的数据是无用的
size 不能超过index.max_result_window这个参数的值,默认10000
深度分页解决方案
sroll
场景
无法实时搜索,所以不适合实时场景
-
原理
类似关系型数据库的cursor,相当于维护了一份当前索引数据的快照,所有查询数据来源于这份快照
sroll可以分为两部分
初始化
// 参数 scroll,表示暂存搜索结果的时间POST /twitter/tweet/_search?scroll=1m{"size": 100,"query": {"match" : {"title" : "elasticsearch"}}}// 会返回一个_scroll_id,_scroll_id用来下次取数据用。
遍历
POST /_search?scroll=1m{"scroll_id":"XXXXXXXXXXXXXXXXXXXXXXX I am scroll id XXXXXXXXXXXXXXX"}
优缺点
search_type:赋值为scan,表示采用 Scroll Scan 的方式遍历,同时告诉 Elasticsearch 搜索结果不需要排序。
- scroll:同上,传时间。
- size:与普通的 size 不同,这个 size 表示的是每个 shard 返回的 size 数,最终结果最大为 number_of_shards * size。
「Scroll Scan与Scroll的区别」
- Scroll-Scan结果「没有排序」,按index顺序返回,没有排序,可以提高取数据性能。
- 初始化时只返回 _scroll_id,没有具体的hits结果
size控制的是每个分片的返回的数据量,而不是整个请求返回的数据量。
search after
基本使用
POST twitter/_search{"size": 10,"query": {"match" : {"title" : "es"}},"sort": [{"date": "asc"},{"_id": "desc"}]}
返回结果
{"took" : 29,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 5,"relation" : "eq"},"max_score" : null,"hits" : [{...},"sort" : [...]},{...},"sort" : [124648691,"624812"]}]}}
上面的请求会为每一个文档返回一个包含sort排序值的数组。
- 这些sort排序值可以被用于search_after参数里以便抓取下一页的数据。
比如,我们可以使用最后的一个文档的sort排序值,将它传递给search_after参数:
GET twitter/_search{"size": 10,"query": {"match" : {"title" : "es"}},"search_after": [124648691, "624812"],"sort": [{"date": "asc"},{"_id": "desc"}]}
若我们想接着上次读取的结果进行读取下一页数据,第二次查询在第一次查询时的语句基础上添加search_after,并指明从哪个数据后开始读取。
基本原理
es维护一个实时游标,它以上一次查询的最后一条记录为游标,方便对下一页的查询,它是一个无状态的查询,因此每次查询的都是最新的数据。
- 由于它采用记录作为游标,因此「SearchAfter要求doc中至少有一条全局唯一变量(每个文档具有一个唯一值的字段应该用作排序规范)」
优点
