【深度分页】ElasticSearch深度分页问题
背景
主要记录ElasticSearch分页方式,以及深度分页的问题,出现的原因和深度分页的替代解决方案
问题
ElasticSearch普通分页查询时通过from+size这两个参数实现,类似于MySQL的limit 分页
from:表示当前页码
size:表示每页展示条数
例如:
#普通分页查询查询 GET index_user_latest/_search { "query": { "match_all": {} }, "from": 0, "size": 10 }
普通分页优缺点:
1.优点:通用,可以指定页码和每页条数
2.缺点:有深度分页的问题
深度分页的问题,参考官方文档
1 原文:By default, you cannot page through more than 10,000 hits using the from and size parameters. To page through more hits, use the search_after parameter.
2 翻译:默认情况下,不能通过from+size分页查询到超过10000条数据,如果要查询到10000条以及更多的数据,可以使用search_after这个参数
深度分页的现象:
1 GET index_user_latest/_search 2 { 3 "query": { 4 "match_all": {} 5 }, 6 "from": 0, 7 "size": 100001 8 }
1 GET index_user_latest/_search 2 { 3 "query": { 4 "match_all": {} 5 }, 6 "from": 9999, 7 "size": 2 8 }
以上两种情况都会导致以下查询错误的情况
解决方案
深度分页的解决方案主要有以下几种
1.(不推荐)max_result_window
该参数可以调整ElasticSearch分页查询的最大结果窗口参数:
该参数默认情况下是:10000,可以通过修改settings参数来调大,缺点是修改参数不能本质上解决深度分页问题,反而会给ES各个shard更大的数据查询压力
例如
1 PUT index_user_latest/_settings 2 { 3 "settings": { 4 "index":{ 5 "max_result_window":20000 6 } 7 } 8 }
修改后查设置
1 GET index_user_latest/_settings
可以看到,max_result_window值被修改成了20000,再次查询20000条数据以内的就不会出现改问题
{ "index_user" : { "settings" : { "index" : { "refresh_interval" : "1s", "number_of_shards" : "1", "provided_name" : "index_user", "max_result_window" : "20000", "creation_date" : "1608530090627", "store" : { "type" : "fs" }, "number_of_replicas" : "0", "uuid" : "7y6AYV6CSWGgVbFOBz3hcg", "version" : { "created" : "6080799" } } } } }
2.search_after
参考官方文档 https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#search-after
特点,查询的是ES实时的数据(相对于Scrooll而言),可以一直进行下一页查询,因此这种方案适合于客户端场景的没有页码的查询,例如推荐刷新等等
缺点:,只能查询下一页,不能上翻页
注意:
1.search_after 查询时from参数必须指定为0或-1,或者直接不指定
2.search_after 需要指定一个唯一的排序字段,通常可以是文档的主键_id字段,同时也可以指定其他的多个字段
示例
1 GET index_user_latest/_search 2 { 3 "query": { 4 "match_all": {} 5 }, 6 "from": 0, 7 "size": 1 8 , 9 "sort": [ 10 { 11 "dbId": { 12 "order": "desc" 13 } 14 }20 ] 21 }
查询结果
1 { 2 "took" : 10, 3 "timed_out" : false, 4 "_shards" : { 5 "total" : 1, 6 "successful" : 1, 7 "skipped" : 0, 8 "failed" : 0 9 }, 10 "hits" : { 11 "total" : 596127, 12 "max_score" : null, 13 "hits" : [ 14 { 15 "_index" : "index_user", 16 "_type" : "t_user", 17 "_id" : "154393210", 18 "_score" : null, 19 "_source" : { 20 //省略 21 }, 22 "sort" : [ 23 18970447 24 ] 25 } 26 ] 27 } 28 }
这里可以使用sort返回的字段作为search_after查询的开始,如下
1 GET index_user_latest/_search 2 { 3 "query": { 4 "match_all": {} 5 }, 6 "search_after":[18970447], 7 "size": 1 8 , 9 "sort": [ 10 { 11 "dbId": { 12 "order": "desc" 13 } 14 } 15 ] 16 }
3.scroll 查询
参考官方文档 https://www.elastic.co/guide/en/elasticsearch/reference/current/scroll-api.html
scroll 查询特点是滚动查询,会在当前生成本次查询的快照版本,无法查询到最新的数据
首次使用srcoll
1 GET index_user_latest/t_user/_search?scroll=1m 2 { 3 "query": { 4 "match": { 5 "nickName": "的" 6 } 7 } 8 }
会返回一个scroll_id,下次查询的时候携带该scroll_id进行查询
1 { 2 "_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAmwY-wWSzM5dzVfUThTMi0ycGZxS1c5U3RVdw==", 3 "took" : 0, 4 "timed_out" : false, 5 "_shards" : { 6 "total" : 1, 7 "successful" : 1, 8 "skipped" : 0, 9 "failed" : 0 10 }, 11 "hits" : {....省略}}
第二次查询
GET /_search/scroll { "scroll" : "1m", "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAmwY-wWSzM5dzVfUThTMi0ycGZxS1c5U3RVdw==" }
注意,scroll 可以使用当前查询快照的过期时间,如果超过当前的过期时间,则查询会报错
注意:使用完成后,可以手动清除当前scoll快照,释放内存