【ClickHouse 技術系列】- ClickHouse 中的巢狀資料結構
簡介:本文翻譯自 Altinity 針對 ClickHouse 的系列技術文章。面向聯機分析處理(OLAP)的開源分析引擎 ClickHouse,因其優良的查詢效能,PB級的資料規模,簡單的架構,被國內外公司廣泛採用。本系列技術文章,將詳細展開介紹 ClickHouse。
前言
本文翻譯自 Altinity 針對 ClickHouse 的系列技術文章。面向聯機分析處理(OLAP)的開源分析引擎 ClickHouse,因其優良的查詢效能,PB 級的資料規模,簡單的架構,被國內外公司廣泛採用。
阿里雲 EMR-OLAP 團隊,基於開源 ClickHouse 進行了系列優化,提供了開源 OLAP 分析引擎 ClickHouse 的雲上託管服務。EMR ClickHouse 完全相容開源版本的產品特性,同時提供叢集快速部署、叢集管理、擴容、縮容和監控告警等雲上產品功能,並且在開源的基礎上優化了 ClickHouse 的讀寫效能,提升了 ClickHouse 與 EMR 其他元件快速整合的能力。訪問
譯者:何源(荊杭),阿里雲端計算平臺事業部高階產品專家
ClickHouse 中的巢狀資料結構
在這篇部落格文章中,我們將瞭解 ClickHouse for MySQL 中的巢狀資料結構,以及如何將其與 PMM 結合使用來檢視查詢。
巢狀結構在關係資料庫管理系統中並不常見。通常情況下,它只是平面表。有時,將非結構化資訊儲存在結構化資料庫中會很方便。
我們正在努力將 ClickHouse 調整為用於 Percona 監控和管理 (PMM) 的長期儲存,尤其是儲存有關查詢的詳細資訊。我們試圖解決的問題之一是,對導致特定查詢失敗的不同錯誤進行計數。
例如,對於日期為 2017-08-17 的查詢:
"SELECT foo FROM bar WHERE id=?"
被執行了 1000 次。其中 25 次失敗的錯誤程式碼為“1212”,8 次失敗的錯誤程式碼為“1250”。當然,在關係資料中進行儲存的傳統方法是建立一個表 "Date, QueryID, ErrorCode, ErrorCnt",然後對這個表執行 JOIN。遺憾的是,列式資料庫在多個 Join 的情況下表現不佳,通常建議使用非規範化表。
我們可以為每個可能的 ErrorCode 建立一個列,但這並不是最優解。可能有成千上萬的列,而且大多數時候它們都是空的。
在這種情況下,ClickHouse 提出了巢狀資料結構。對於我們的情況,這些可以定義為:
CREATE TABLE queries ( Period Date, QueryID UInt32, Fingerprint String, Errors Nested ( ErrorCode String, ErrorCnt UInt32 ) )Engine=MergeTree(Period,QueryID,8192);
這個解決方案有明顯的問題:我們如何在這個表中插入資料?我們如何提取它?
我們先從 INSERT 開始。插入可能如下所示:
INSERT INTO queries VALUES ('2017-08-17',5,'SELECT foo FROM bar WHERE id=?',['1220','1230','1212'],[5,6,2])
這意味著 2017-08-17 期間插入的查詢出現了 5 次錯誤 1220,6 次錯誤 1230,2 次錯誤 1212。
那麼在不同的日期,它可能會產生不同的錯誤:
INSERT INTO queries VALUES ('2017-08-18',5,'SELECT foo FROM bar WHERE id=?',['1220','1240','1258'],[3,2,1])
讓我們看一下 SELECT 資料的方法。非常基礎的 SELECT:
SELECT * FROM queries |_____Period_|_QueryID_|_Fingerprint_|_Errors.ErrorCode_______|_Errors.ErrorCnt_| | 2017-08-17 | 5 | SELECT foo | ['1220','1230','1212'] | [5,6,2] | | 2017-08-18 | 5 | SELECT foo | ['1220','1240','1260'] | [3,16,12] | |____________|_________|_____________|________________________|_________________|
如果我們想使用更熟悉的表格輸出,則可以使用 ARRAY JOIN 擴充套件:
SELECT * FROM queries ARRAY JOIN Errors ┌─────Period─┬─QueryID─┬─Fingerprint─┬─Errors.ErrorCode─┬─Errors.ErrorCnt─┐ │ 2017-08-17 │ 5 │ SELECT foo │ 1220 │ 5 │ │ 2017-08-17 │ 5 │ SELECT foo │ 1230 │ 6 │ │ 2017-08-17 │ 5 │ SELECT foo │ 1212 │ 2 │ │ 2017-08-18 │ 5 │ SELECT foo │ 1220 │ 3 │ │ 2017-08-18 │ 5 │ SELECT foo │ 1240 │ 16 │ │ 2017-08-18 │ 5 │ SELECT foo │ 1260 │ 12 │ └────────────┴─────────┴─────────────┴──────────────────┴─────────────────┘
但是,通常我們希望看到多個期間的聚合,這可以通過傳統的聚合函式來完成:
SELECT QueryID, Errors.ErrorCode, SUM(Errors.ErrorCnt) FROM queries ARRAY JOIN Errors GROUP BY QueryID, Errors.ErrorCode ┌─QueryID─┬─Errors.ErrorCode─┬─SUM(Errors.ErrorCnt)─┐ │ 5 │ 1212 │ 2 │ │ 5 │ 1230 │ 6 │ │ 5 │ 1260 │ 12 │ │ 5 │ 1240 │ 16 │ │ 5 │ 1220 │ 8 │ └─────────┴──────────────────┴──────────────────────┘
如果我們別出心裁,每個 QueryID 只返回一行,我們也可以這麼做:
SELECT QueryID, groupArray((ecode, cnt)) FROM ( SELECT QueryID, ecode, sum(ecnt) AS cnt FROM queries ARRAY JOIN Errors.ErrorCode AS ecode, Errors.ErrorCnt AS ecnt GROUP BY QueryID, ecode ) GROUP BY QueryID ┌─QueryID─┬─groupArray(tuple(ecode, cnt))──────────────────────────────┐ │ 5 │ [('1230',6),('1212',2),('1260',12),('1220',8),('1240',16)] │ └─────────┴────────────────────────────────────────────────────────────┘
結論
ClickHouse 提供了靈活的方式來儲存資料,儘管它是一個列式資料庫,但可以實現較低的結構化程度,並提供各種函式來提取和聚合資料。
後續
您已經瞭解了在 ClickHouse 中處理實時更新相關內容,本系列還包括其他內容:
- ClickHouse 中的巢狀資料結構(本文)
原文連結
本文為阿里雲原創內容,未經允許不得轉載。