本文基于elasticsearch java客户端elasticsearch-5.5.2.jar
在工作中遇到一些问题,存储大量数据,本来是计划用OpenTSDB存储这些数据,因为这些数据可以描述为点,具有比较明确的时序特性,但实际中用的OpenTSDB不稳定,并且调研发现ES也可以存这批数据,相对来讲扩展性更好,但需要做好跨索引的工作。
查询
json报文格式:
{
"query": {
"bool": {
"must": [
{
"term": {
"src": "10.10.169.145"
}
},
{
"term": {
"app": "中文"
}
},
{
"term": {
"appid": "test"
}
},
{
"range": {
"timestamp.long": {
"gt": "1544510518758",
"lt": "1544511518758"
}
}
}
],
"should": [
{
"term": {
"id": "test.id"
}
}
]
}
},
"from": 0,
"size": 10000,
"sort": [],
"aggs": {
}
}
}
以上报文匹配appid为test,app为中文,src为10.10.169.145,timestamp时间范围为1544510518758到1544511518758,并且id可以为test.id的文档,其中包括must查询和should查询,must查询为全匹配,should可以理解为或,其它查询还有mustnot为非,wildcardQuery查询为模糊匹配。
String index = "index";
String aes1 = "x*";
String aes2 = "g*";
String appid="";
String appgroup="";
long startTime = Long.parseLong(start);
long endTime = Long.parseLong(end);
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.must(QueryBuilders.wildcardQuery("src", aes1));
queryBuilder.must(QueryBuilders.wildcardQuery("target", aes2));
queryBuilder.must(QueryBuilders.termQuery("appid", appid));
queryBuilder.must(QueryBuilders.termQuery("appgroup", appgroup));
queryBuilder.must(QueryBuilders.rangeQuery("timestamp").gte(startTime).lte(endTime));
ESClient client = new ESClient("127.0.0.1:9300", ""); // 连接到客户端
SearchResponse sResponse = client.getClient().prepareSearch(index) // 要查询的索引,如果是跨索引查询输入String数组
.setTypes(IndexMgr.TEST_Table) // table类型,索引之下的一个类型,对应放数据时候的类型
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH) //查询方式
.setQuery(queryBuilder) //查询条件
.setSize(10000) // 返回的doc的个数
.get(TimeValue.timeValueMillis(5000)); // 超时时间
分组及聚合
有时候查询的时候需要用的分组,并且聚合的情况
{
"query":{
"bool":{
"must":[
{
"term":{
"src":"10.10.169.145"
}
},
{
"term":{
"app":"中文"
}
},
{
"term":{
"appid":"test"
}
},
{
"range":{
"timestamp.long":{
"gt":"1544510518758",
"lt":"1544511518758"
}
}
}
],
"should":[
{
"term":{
"id":"test.id"
}
}
]
}
},
"from":0,
"size":0,
"sort":[
],
"aggs":{
"urilist":{
"terms":{
"field":"uri"
},
"aggs":{
"total":{
"sum":{
"field":"total"
}
},
"add":{
"sum":{
"field":"add"
}
}
}
}
}
}
以上是一个查询并且聚合的报文,query中是查询文档的一系列条件,aggs中是分组和聚合相关报文,urilist为分组的名字,自己取名,uri为分组的字段,里面还有个aggs,代表对已经分组的文档进行内部聚合,这里的聚合对total和add两个字段作了加和。
String index = "index";
String aes1 = "x*";
String aes2 = "g*";
String appid="";
String appgroup="";
long startTime = Long.parseLong(start);
long endTime = Long.parseLong(end);
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.must(QueryBuilders.wildcardQuery("src", aes1));
queryBuilder.must(QueryBuilders.wildcardQuery("target", aes2));
queryBuilder.must(QueryBuilders.termQuery("appid", appid));
queryBuilder.must(QueryBuilders.termQuery("appgroup", appgroup));
queryBuilder.must(QueryBuilders.rangeQuery("timestamp").gte(startTime).lte(endTime));
TermsAggregationBuilder srclist = AggregationBuilders.terms("urilist").field("uri").size(10000); // 设置返回的分组数,如果不设置默认是10
AggregationBuilder total =AggregationBuilders.sum("total").field("total");
AggregationBuilder add =AggregationBuilders.sum("add").field("add");
srclist.subAggregation(total);
srclist.subAggregation(add);
ESClient client = new ESClient("127.0.0.1:9300", ""); // 连接到客户端
SearchResponse sResponse = client.getClient().prepareSearch(index) // 要查询的索引,如果是跨索引查询输入String数组
.setTypes(IndexMgr.TEST_Table) // table类型,索引之下的一个类型,对应放数据时候的类型
.setSearchType(SearchType.DFS_QUERY_THEN_FETCH) //查询方式
.setQuery(queryBuilder) //查询条件
.setSize(0) // 返回的doc的个数为0,因为只需要分组结果
.addAggregation(agg) //添加聚合
.get(TimeValue.timeValueMillis(5000)); // 超时时间
TermsAggregation聚合器默认是查询top N的项目,如果不设置聚合大小,默认是10。
相关测试及注意问题
- 索引分片大小合适的大小为50G左右
- 跨索引查询的情况下,会比单索引情况下慢,以下单索引查询,跨20个索引,跨60个索引查询,以及各文档个数情况下测试的一些数据(没有足够考虑缓存的情况下,不能说很精确),但能看出几个趋势:
- 单索引查询情况下在测试文档个数范围内,文档个数对查询效率的影响比较小;
- 多索引查询比单索引查询慢,大概在3到5倍
- 同文档数情况下聚合比查询要快(可以看看倒排索引相关文档,我理解为已经天然分好了组)
- 单索引聚合和多索引聚合相比,多索引聚合要慢,但差距比多索引查询比单索引查询小很多(1到2倍)
- 建立合适的索引,做好分索引的工作,如果数据不长期存放是最好的