一:ES的存储和查询原理
Elasticsearch中数据都存储在分片中,当执行搜索时每个分片独立搜索后,数据再经过整合返回。
二:如何分页查询呢?
(1)抛出需求问题
按照一般的查询流程来说,如果我想查询前10条数据:
1 客户端请求发给某个节点
2 节点再转发给某个分片,查询每个分片上的前10条
3 各个分片的查询结果返回给节点,然后再整合数据,提取前10条
4 节点再返回给请求客户端
那么当我想要查询第11条到第20条的数据该怎么办呢?这个时候就用到分页查询了。
(2)两种解决方式
1.第一种from-size"浅"分页
概念:"浅分页”是查的页数比较小;"深分页”是查的比较深。
举个例子:百度最多查询前76页,为什么只有76页,而没有第1万页,假如查询第1万页就是深度分页了。
带着为啥百度只能查询76页的疑问,我们分析下浅分页和深分页的原理是什么?
假设每页大小是10条,查第77页761-770条数据。
浅分页的原理就是要查询每个分片上的前77页的前770条数据,然后不要前760条,只返回761-770的数据。这样其实白白浪费了前760条的查询。
查询的方法如:
{
"from" : 760, "size" : 10,
"query" : {
"term" : { "user" : "yao" }
}
}
其中,from定义了目标数据的偏移值,size定义当前返回的事件数目。
默认from为0,size为10,即所有的查询默认仅仅返回前10条数据。
性能问题:做过测试,越往后的分页,执行的效率越低。
通过下图可以看出,刨去一些异常的数据,总体上还是会随着from的增加,消耗时间也会增加。而且数据量越大,效果越明显!
也就是说,分页的偏移值越大,执行分页查询时间就会越长!
2.第二种scroll“深”分页
相对于from和size的分页来说,使用scroll可以模拟一个传统数据的游标,记录当前读取的数据条数位置。这个分页的用法,缺点是不实时查询数据,优点是一次性查询大量的数据(甚至是全部的数据)。
因为这个scroll相当于维护了一份当前索引段的快照信息,这个快照信息是你执行这个scroll查询时的快照。**为什么不实时?**因为快照没有更新最新的索引,在这个查询后的任何新索引进来的数据,都不会在这个快照中查询到。但是它相对于from和size,不是查询所有数据然后剔除不要的部分,而是记录一个读取的位置,保证下一次快速继续读取。
看到这里,总结下两者的区别:
(1)从查询数据的大小来看。form和size是全量查询数据,然后舍弃掉无用数据;scroll是只查询所需数据,因为它有游标记录了位置。
(2)从查询数据的实时性来看。form和size是实时查询;scroll因为快照没有及时更新最新插入数据的索引,es为查询而生,插入不是强项,所有插入都要构建索引。而快照属于拷贝,拷贝之后有新的数据插入并构建了索引,快照并没有及时重新拷贝新的。
如何选择哪种方式进行分页?
(1)使用上的选择,form和size已经满足大部分的需求。form和size每次只能查询1万条。百度的76页,每页6条只有456条数据。对于平时的业务系统每页10条,能够满足查询1000页了,如果超出了1000页,谁会看超出1000页的数据呢?已经没有业务使用意义了,那么肯定是产品定义错了需求,需要页面加过滤条件,比如时间字段,分多次查询几千页,每次看1000页。
API使用方法如:
curl -XGET 'localhost:9200/twitter/tweet/_search?scroll=1m' -d '
{
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
会自动返回一个_scroll_id,通过这个id可以继续查询(实际上这个ID会很长哦!):
curl -XGET ‘localhost:9200/_search/scroll?scroll=1m&scroll_id=c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1’
注意,我在使用1.4版本的ES时,只支持把参数放在URL路径里面,不支持在JSON body中使用。
测试from&size VS scroll的性能
首先呢,需要在java中引入elasticsearch-jar,比如使用maven:
这里重点不是讲ES的使用,ES8.x已经舍弃了TransportClient 客户端的用法,建议使用Java REST Client客户端,本文中还是用的旧的TransportClient ,这不是重点,重点是知道两种分页方式
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>1.4.4</version>
</dependency>
然后初始化一个client对象:
private static TransportClient client;
private static String INDEX = "index_name";
private static String TYPE = "type_name";
public static TransportClient init(){
Settings settings = ImmutableSettings.settingsBuilder()
.put("client.transport.sniff", true)
.put("cluster.name", "cluster_name")
.build();
client = new TransportClient(settings).addTransportAddress(new InetSocketTransportAddress("localhost",9300));
return client;
}
public static void main(String[] args) {
TransportClient client = init();
//这样就可以使用client执行查询了
}
然后就是创建两个查询过程了 ,下面是from-size分页的执行代码:
System.out.println("from size 模式启动!");
Date begin = new Date();
long count = client.prepareCount(INDEX).setTypes(TYPE).execute().actionGet().getCount();
SearchRequestBuilder requestBuilder = client.prepareSearch(INDEX).setTypes(TYPE).setQuery(QueryBuilders.matchAllQuery());
for(int i=0,sum=0; sum<count; i++){
SearchResponse response = requestBuilder.setFrom(i).setSize(50000).execute().actionGet();
sum += response.getHits().hits().length;
System.out.println("总量"+count+" 已经查到"+sum);
}
Date end = new Date();
System.out.println("耗时: "+(end.getTime()-begin.getTime()));
下面是scroll分页的执行代码,注意啊!scroll里面的size是相对于每个分片来说的,所以实际返回的数量是:分片的数量*size
System.out.println("scroll 模式启动!");
begin = new Date();
SearchResponse scrollResponse = client.prepareSearch(INDEX)
.setSearchType(SearchType.SCAN).setSize(10000).setScroll(TimeValue.timeValueMinutes(1))
.execute().actionGet();
count = scrollResponse.getHits().getTotalHits();//第一次不返回数据
for(int i=0,sum=0; sum<count; i++){
scrollResponse = client.prepareSearchScroll(scrollResponse.getScrollId())
.setScroll(TimeValue.timeValueMinutes(8))
.execute().actionGet();
sum += scrollResponse.getHits().hits().length;
System.out.println("总量"+count+" 已经查到"+sum);
}
end = new Date();
System.out.println("耗时: "+(end.getTime()-begin.getTime()));
我这里总的数据有33万多,分别以每页5000,10000,50000的数据量请求,得到如下的执行时间:
可以看到仅仅30万,就相差接近一倍的性能,更何况是如今的大数据环境…因此,如果想要对全量数据进行操作,快换掉fromsize,使用scroll吧!
最后总结:ES更新版本很快,1.x,2.x,3.x,4.x,5.x,6.x,我用的6.x
来源:CSDN
作者:姚春辉实验室
链接:https://blog.csdn.net/yaochunhui2020/article/details/103968535