一、Scroll滚动查询介绍
ElasticSearch中在进行普通的查询时,默认只会查询出来10条数据。我们通过设置ElasticSearch中的size
可以将最终的查询结果从10
增加到10000
。但这时候如果我们需要查询的数据大于10000条怎么办呢?这时候有两种方法:深度分页
和滚动查询
。在这里我们优选选择滚动查询
,因为深度分页越往后查性能越低,极其耗费内存和CPU。
在介绍滚动查询之前,我们先简单了解下深度分页
深度分页其实就是用from
和size
两个关键字实现的。如下图所示,from
关键字可以指定从哪个位置开始搜索,size
关键字可以指定搜索几条数据,但深度分页的搜索很深,对性能方面会带来很大的影响, 因此这种方式不推荐
使用。
GET /online_house_achieve/house/_search
{
"query": {"match_all": {}},
"from": 10000,
"size": 5
}
然后我们来了解下滚动查询
滚动查询,和关系型数据库中的游标
有点类似,因此也叫游标查询
。也相当于一个快照
,它是ElasticSearch中提供的一种查询大数据量
的方式。
二、Kibana上操作
要想使用滚动查询,我们只需要在_search
后面加上scroll
就好了。
GET /online_house_achieve/house/_search?scroll=1m
{
"query": {"match_all": {}},
"size": 1
}
这里有几个注意点
- scroll表示这是一个scroll滚动查询。
- scroll=
1m
表示查询的结果数据在ElasticSearch服务器中过期时间为1min
。 - 查询完会返回一个
_scroll_id
,该字段其实就相当于一个书签,在我们之后的查询中需要带着这个书签,就可以一直往后根据设置的size大小获取数据(前提是在设置的过期时间之内)。那问题来了,我们该如何使用这个
_scroll_id
实现进一步的滚动查询
呢?我们直接看下面代码。
GET /_search/scroll/
{
"scroll":"1m",
"scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAa5Fm9hLW9TeGtTU2NTWEI2bFpNbEJ0clEAAAAAAAAGtxZvYS1vU3hrU1NjU1hCNmxaTWxCdHJRAAAAAAAABroWb2Etb1N4a1NTY1NYQjZsWk1sQnRyUQAAAAAAAAa4Fm9hLW9TeGtTU2NTWEI2bFpNbEJ0clEAAAAAAAAGuxZvYS1vU3hrU1NjU1hCNmxaTWxCdHJR"
}
将获取的scroll_id
作为条件继续查询即可,不需要再指定索引和类型。因为scroll_id
具有唯一性
,在过期时间内,之后查询的scroll_id是不变的。
三、SpringBoot中操作
1.先在pom.xml里面引入依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2.然后在SpringBoot项目中增加以下配置。
这里我是用HTTP的方式进行连接,端口是9200。如果你们是用TCP方式进行连接的话,端口记得改成9300。
@Configuration
public class ESRestClientConfig extends AbstractElasticsearchConfiguration {
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("127.0.0.1:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}
3.然后写单元测试代码,实现滚动查询。
@Test
public void testESScroll() throws IOException {
SearchRequest searchRequest = new SearchRequest();
// 构造ElasticSearch查询条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.size(1).query(QueryBuilders.matchAllQuery());
// 设置scroll超时时间(10min)
Scroll scroll = new Scroll(TimeValue.timeValueMinutes(10L));
// 指定查询的索引和类型
searchRequest.indices("online_house_achieve").types("house").scroll(scroll);
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 获取第一次查询的结果,并拿到scrollId滚动id
SearchHit[] scrollHits = response.getHits().getHits();
for (SearchHit hit : scrollHits) {
logger.info(hit.getSourceAsString());
}
//记录滚动id。
String scrollId = response.getScrollId();
//滚动查询部分,将从第2条数据开始取。
scrollHits = response.getHits().getHits();
while (scrollHits != null && scrollHits.length > 0 ) {
//构造滚动查询条件
SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
searchScrollRequest.scroll(scroll);
//响应必须是上面的响应对象,需要对上一层进行覆盖。
response = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
scrollId = response.getScrollId();
scrollHits = response.getHits().getHits();
for (SearchHit hit : scrollHits) {
logger.info(hit.getSourceAsString());
}
}
// 数据获取完毕后需要清除滚动,否则影响下次查询
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
ClearScrollResponse clearScrollResponse = restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
//清除滚动是否成功
boolean isSuccess = clearScrollResponse.isSucceeded();
logger.info("=====================>清楚滚动scroll是否成功:{}",isSuccess);
}
四、总结
- 滚动查询是建立在普通查询基础上的。
- 滚动查询相当于快照,如果在使用scroll进行滚动查询期间有增删改的操作,那么查询结果是获取不到最新的数据的。
- 深度分页和滚动查询优先使用滚动查询,性能更优,CPU资源耗费更少。
- 使用完滚动查询后要记得
清除滚动
,以免影响下次使用。