本文基於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倍)
- 建立合適的索引,做好分索引的工作,如果數據不長期存放是最好的