ES分页
本篇文章将会讲解ES两种分页查询方法以及优缺点
注意:以下文章中的命令和java代码均基于ES5.3.0版本
一、from/size分页
官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/5.3/search-request-from-size.html
查询的时候带上from和size两个参数即可
- from参数默认为0,size参数默认为10
- 查询结果索引 from + size 的值不能超出index.max_result_window,默认该值是10000
- 如果想做深度分页,可以考虑使用 Scroll 或者Search After 方式。
1.修改默认窗口大小
PUT请求 `{{es-host}}/索引名字/_settings
{
"index": {
"max_result_window": 1000000
}
}
可以适当增大窗口值大小方便常用分页查询
2.JAVA API
transportClient.prepareSearch(索引名)
.setQuery(mixQuery)
.setFrom((pageNum - 1) * pageSize)
.setSize(pageSize)
.setFetchSource(true);
3.优缺点
优点就是简单,和mysql的分页很相似
缺点也很明显,首先,分页受限于结果窗口大小,而该值默认值仅仅是10000,就算可以调整该分页大小,仍然让人心里不爽快,再者,随着分页from的值越来越大,效率会越来越低。from/size的分页原理是先查询出所有结果,然后根据from和size进行结果截取,所以势必会随着分页深度的增加效率越来越低。最后,由于需要将查询结果都加载到内存,有可能会出现内存爆掉的情况发生。
二、Scroll分页
从from/size的官网为文档上来看,如果需要“深分页”,最好使用Scroll or Search After API。
官网文档:https://www.elastic.co/guide/en/elasticsearch/reference/5.3/search-request-scroll.html
scroll效率之所以高,是因为它内部维护着一个“游标”,类似于指针的东西,每次查询完size个数据,游标就会移动到查询数据的尾部等待下次查询。
1.初次请求
请求方式
POST
请求地址
{{es-host}}/website/_search?scroll=1m
请求体
{
"size": 1,
"query": {
"match_all": {}
}
}
响应结果
{
"_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAANLb6Flg4V2VGRndwUW91SnBVWUlxcTJzc2cAAAAAADS2-RZYOFdlRkZ3cFFvdUpwVVlJcXEyc3NnAAAAAAA0tvgWWDhXZUZGd3BRb3VKcFVZSXFxMnNzZwAAAAAANLb8Flg4V2VGRndwUW91SnBVWUlxcTJzc2cAAAAAADS2-xZYOFdlRkZ3cFFvdUpwVVlJcXEyc3Nn",
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 5,
"max_score": 1.0,
"hits": [
{
"_index": "website",
"_type": "blog",
"_id": "AXoGPweiCycsj4NKux3V",
"_score": 1.0,
"_source": {
"name": "test"
}
}
]
}
}
初次请求和正常的请求没什么区别,就加上一个参数scroll=1m
即可,表示本次scroll游标一分钟内有效。
接着,拿到的结果中除了正常返回的数据,还多了一个_scroll_id
参数,下次请求就可以直接使用该参数进行分页查询了。
2.以后的请求
请求方式
GET
请求地址
{{es-host}}/_search/scroll?scroll=1m&scroll_id=DnF1ZXJ5VGhlbkZldGNoBQAAAAAANL04FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9NhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtBAAAAAAA0vTcWYmtGNjlWSkVSbTIyNm9zazlUX3VLQQAAAAAANL05FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9OhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtB
请求体
无
响应结果
{
"_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAANL04FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9NhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtBAAAAAAA0vTcWYmtGNjlWSkVSbTIyNm9zazlUX3VLQQAAAAAANL05FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9OhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtB",
"took": 1,
"timed_out": false,
"terminated_early": true,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 5,
"max_score": 1.0,
"hits": [
{
"_index": "website",
"_type": "blog",
"_id": "AXlizj8kCycsj4NKuvL8",
"_score": 1.0,
"_source": {
"name": "cve-2015-1427"
}
}
]
}
}
可以看到,再次请求的时候不需要再指定索引名称和相关的复杂查询条件,直接告诉ES上一次查询的游标_scroll_id即可。
3.JAVA API
SearchRequestBuilder searchRequestBuilder = transportClient.prepareSearch(index.split(","))
.setQuery(mixQuery)
.setSize(pageSize)
.setFetchSource(true)
.setScroll(TimeValue.timeValueMinutes(1))
.addSort(fieldSortBuilder);
只需要加上 .setFetchSource(true)即可。
第一次查询之后要正常处理结果数据,并非像是某些文章中讲的只是返回scroll_id
之后的查询则就简单多了
scrollId = searchResponse.getScrollId();
while (searchResponse.getHits().getHits().length > 0) {
//处理结果数据
SearchScrollRequestBuilder searchScrollRequestBuilder = transportClient.prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(1));
searchResponse = searchScrollRequestBuilder.execute().actionGet();
scrollId = searchResponse.getScrollId();
}
三、Search After分页
文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/5.3/search-request-search-after.html
没用过,略。