elasticsearch聚合及查詢詳細報文

本文基於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。

相關測試及注意問題

  1. 索引分片大小合適的大小爲50G左右
  2. 跨索引查詢的情況下,會比單索引情況下慢,以下單索引查詢,跨20個索引,跨60個索引查詢,以及各文檔個數情況下測試的一些數據(沒有足夠考慮緩存的情況下,不能說很精確),但能看出幾個趨勢:
  • 單索引查詢情況下在測試文檔個數範圍內,文檔個數對查詢效率的影響比較小;
  • 多索引查詢比單索引查詢慢,大概在3到5倍
  • 同文檔數情況下聚合比查詢要快(可以看看倒排索引相關文檔,我理解爲已經天然分好了組)
  • 單索引聚合和多索引聚合相比,多索引聚合要慢,但差距比多索引查詢比單索引查詢小很多(1到2倍)
  1. 建立合適的索引,做好分索引的工作,如果數據不長期存放是最好的
    在這裏插入圖片描述

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章