小試牛刀ElasticSearch大數據聚合統計

        ElasticSearch相信有不少朋友都瞭解,即使沒有了解過它那相信對ELK也有所認識E即是ElasticSearch。ElasticSearch最開始更多用於檢索,作爲一搜索的集羣產品簡單易用絕對是一個非常不錯的選擇,其實本人早在ElasticSearch v0.2的時候就使用,一轉眼數年過去現在都7.X了。

        其實ElasticSearch除了提供強大的集羣化搜索服務外,它提供一個aggregation功能會再一次讓你受到它的強大,aggregation是一個數據統計彙總功能,表面上這功能在關係數據庫上也可以做,但結合分詞建維度就更能體現出它的靈活之處。

關係數據庫問題

        拿產品訂單爲例,它有產品分類,不同的規格,銷售人,客戶和地區等;然而這些信息在設計上都是歸納到不同的表中,如果要針對這些不同的信息來統計訂單銷售情況那相信是一件非常繁瑣和效率極其低下的工作(先不說數據數千萬了上億或更大規模,就算幾十上百萬數據這個關係數據的SQL查詢也夠受了)。即便可以把數據抽取並歸納起來做統計,但隨着新的數據維度增加新的維度字段重新調用。

無維度字段?

        在數據統計每個維度都對一個信息列來存儲,這樣加入維度必須就需要添加信息列。如果用一個字段存儲所有維度信息呢?顯然這種想法在傳統關係數據庫中也不可能的,因爲無法做表的關聯和維度區分,其實不要說傳統數據庫很多數據庫都無法在一個字符中拆分出不同的維度出來,除非加入程序來切分,但這種法在數據規模大的情況必然是不可取的!

        如果用一個字段就能存儲所有維度,那就意味着以後加入新的維度數據也無須調整結構和程序就實現新維護度數據的統計處理。這看上去多麼美好,似乎也很難實現,但ElasticSearch能解決這一問題。

試驗

        首先ElasticSearch是一個搜索引擎,它最擅長的工作是對內容進行分詞並構建索引;在這機制下可以對一個字段的信息進行拆分並存儲到索引上。通過這一特性同樣可以把一個字段的信息切分成N個維度的信息,然後存儲到索引;只要有了單一的維度索引那接下來針對不同維護的彙總統計就簡單了。

在單節點的ElasticSearch上創建了5千萬條產品銷售數據;然後Tag字段存儲對應的維度信息,每個維度通過/來區分,分別有:customer,employee,country和category.有了這些信息,接下來的工作是嘗試使用Aggr功能來完成相應的彙總

var query = db.Index.CreateQuery();
query.Prefix("Tag", "客戶");
var aggs = db.Index.CreateAggs("customer_group", 
     Elasticsearch.Search.AggsType.terms, "Tag");
aggs.SubAggs("sum_quantity", Elasticsearch.Search.AggsType.sum, "Quantity");
aggs.SubAggs("sum_total", Elasticsearch.Search.AggsType.sum, "Total");
aggs.Size(5);
aggs.Query = query;
var items = await aggs.Execute<OrderRecord>();

代碼並不複雜,查詢Tag標籤存在customer的數據,並對它們進行一個分組,最後再彙總出對應的Quantity和Total信息;最後獲取排在最前面的5條數據。

效率

        ElasticSearch做這方面的效率怎樣呢?部署在一個節點上,分別彙總了客戶,國家和員工。

5千萬條(單機單節點)

5千萬條(單機雙節點)

 這個時間是在不停更新索引下同時做統計的結果,當在索引不更新的情況其二次處理效率會高上幾倍。

靜態歷史數據

        上面紹了ElasticSearch對大數據一個聚合效率做了一個測試,那測試是基於動態數據測試,即在聚合測試的過程中同時大量更新索引數據;接下來做的測試則是針對固定的歷史數據,在聚合測試過程中不進行數據更新。

測試數據環境

        5千萬條件產品銷售數據,分佈在2000-2020間,所有數據部署在單機雙節點的服務中。

測試過程

        分別彙總每一年的員工,國家和分類數據,並顯示最前面的3條記錄。

int top = 3;
for (int i = 2000; i < 2020; i++)
{
    DateTime start = new DateTime(i, 1, 1);
    DateTime end = new DateTime(i + 1, 1, 1);
    var result = await db.AggsTag("國家", top, start, end, null);
    Console.WriteLine($"|  {result.Title} use {result.UseTime:###,###.00}ms");
    Console.WriteLine($"|-{"".PadLeft(89, '-')}|");
    foreach (SummaryItem item in result.Items)
    {
        Print(item);
    }

    result = await db.AggsTag("分類", top, start, end, null);
    Console.WriteLine($"|  {result.Title} use {result.UseTime:###,###.00}ms");
    Console.WriteLine($"|-{"".PadLeft(89, '-')}|");
    foreach (SummaryItem item in result.Items)
    {
        Print(item);
    }

    result = await db.AggsTag("員工", top, start, end, null);
    Console.WriteLine($"|  {result.Title} use {result.UseTime:###,###.00}ms");
    Console.WriteLine($"|-{"".PadLeft(89, '-')}|");
    foreach (SummaryItem item in result.Items)
    {
        Print(item);
    }
}

測試結果

 從測試結果來看效率非常出色,每個年分類聚合統計所損耗的時候大概在0.1秒。

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