數據類型
- 儘量用數值型
- 建表時能用數值型或日期時間型表示的字段,就不要用字符串——全String類型在以Hive爲中心的數倉建設中常見,但CK環境不應受此影響。
- 直接用DataTime
- 直接用DateTime表示時間列,而不是用整形的時間戳。因爲CK中DateTime的底層就是時間戳,效率高,可讀性好,且轉換函數豐富。
- 不用Nullable
- 官方已經指出Nullable類型幾乎總是會拖累性能,因爲存儲Nullable列時需要創建一個額外的文件來存儲
NULL
的標記,並且Nullable列無法被索引。因此除非極特殊情況,應直接使用字段默認值表示空,或者自行指定一個在業務中無意義的值(例如用-1表示沒有商品ID)。
- 官方已經指出Nullable類型幾乎總是會拖累性能,因爲存儲Nullable列時需要創建一個額外的文件來存儲
分區和索引
- 事實表必須分區,分區粒度根據業務特點決定,不宜過粗或過細。我們當前都是按天分區,按小時、周、月分區也比較常見(系統表中的query_log、trace_log表默認就是按月分區的)。
- 必須指定索引列,在絕大多數查詢的
WHERE
語句中都會用到的列適合作爲索引。CK的索引非MySQL的B樹索引,而是類似Kafka log風格的稀疏索引,故不用考慮最左原則,但是建議日期列和區分度較低的列在前,區分度較高的列在後。
表參數
- 生產環境中提供線上服務的表均採用複製表與分佈式表相結合,即Replicated*MergeTree+Distributed引擎。分佈式表的表名爲本地表名加上
_all
後綴。 - 如果表中不是必須保留全量歷史數據,建議指定TTL,可以免去手動過期歷史數據的麻煩。TTL也可以通過
ALTER TABLE
語句隨時修改。 - 建議指定
use_minimalistic_part_header_in_zookeeper = 1
設置項,能夠顯著壓縮表元數據在ZooKeeper中的存儲。該項也可以寫入config.xml中的<merge_tree>一節。
查詢相關事項
單表查詢
- 所有應用層查詢禁止
SELECT *
。 - 查詢分區表必須指定分區(所謂partition pruning),不能全表查詢。
- 大規模數據集上的ORDER BY要加LIMIT限制。
- 結果集上的簡單運算(例如
SELECT pv, uv, pv / uv as ratio...
中的ratio)可以在前端展示時再進行,減少SQL中不必要的虛擬列。 - 業務場景非強制要求100%準確的基數計量,應該用uniq()函數而不是uniqExact()函數或DISTINCT關鍵字。uniq()底層採用HyperLogLog實現,能夠以低於1%的精度損失換來極大的性能提升。
- 能夠重用的模式化查詢(如固定刷新的BI報表、熱力圖等)一定要做成物化視圖,並在物化視圖上查詢出結果,可以避免大量的重複計算。關於其用法,可參見之前寫過的《物化視圖簡介與ClickHouse中的應用示例》。
多表查詢
- 當兩表關聯查詢只需要從左表出結果時,建議用IN而不是JOIN,即寫成
SELECT ... FROM left_table WHERE join_key IN (SELECT ... FROM right_table)
的風格。 - 不管是LEFT、RIGHT還是INNER JOIN操作,小表都必須放在右側。因爲CK默認在大多數情況下都用hash join算法,左表固定爲probe table,右表固定爲build table且被廣播。
- CK的查詢優化器比較弱,JOIN操作的謂詞不會下推,因此一定要先做完過濾、聚合等操作,再在結果集上做JOIN。這點與我們寫其他平臺SQL語句的習慣很不同,初期尤其需要注意。
- 兩張分佈式表上的IN和JOIN之前必須加上GLOBAL關鍵字。如果不加GLOBAL關鍵字的話,每個節點都會單獨發起一次對右表的查詢,而右表又是分佈式表,就導致右表一共會被查詢N2次(N是該分佈式表的shard數量),這就是所謂的查詢放大,會帶來不小的overhead。加上GLOBAL關鍵字之後,右表只會在接收查詢請求的那個節點查詢一次,並將其分發到其他節點上。
寫入相關事項
- 寫入分佈式表的底表,而不直接寫分佈式表。在之前的《ClickHouse複製表、分佈式表機制與使用方法》一文中已有說明。
- 不要做小批量零碎的寫入,每批次至少千條級別,避免給merge造成太大壓力。
- 不要同時寫入太多個分區,或者寫入過快(官方給出的閾值爲1秒1次),容易因爲merge的速度跟不上parts生成的速度而報出"too many parts"的錯誤。如果正常情況下還會出現此錯誤,建議在CPU資源允許的情況下適當調大後臺任務線程數
background_pool_size
,默認值爲16。
運維相關事項
CPU
CK的“快”與其對CPU的積極利用密不可分,所以CPU的單核性能和多核性能都要儘量好一點,16核32線程左右且帶較高的睿頻比較合適。CK設置中的max_threads
參數控制單個查詢所能利用的CPU線程數,默認與本機CPU的物理核心數相同,如果服務器是CK獨佔的,那麼就不用改,否則就改小些。
在監控集羣時,CPU指標也是最重要的。實測當單個CK Server節點的CPU使用率超過70%時,服務就不太穩定了。
內存
官方文檔建議單機物理內存128G左右。實測CK在我們的應用場景下內存佔用並不激進,每線程對應1G內存非常綽綽有餘,即max_threads
設爲20的話,max_memory_usage
參數設爲20G(懶得打辣麼多0了)。爲了不干擾系統的正常運行,也應配置所有查詢能利用的最大內存參數max_memory_usage_for_all_queries
,取物理內存的80%左右即可。
另外,CK在執行GROUP BY聚合邏輯的過程中很有可能超出內存限制,因此也建議設置max_bytes_before_external_group_by
參數。在內存佔用超出此閾值之後,就會spill到磁盤繼續操作,且性能沒有降低特別多。官方建議將它設置爲max_memory_usage
的一半。
存儲
CK不太挑存儲介質,普通7200rpm SATA HDD都可以用,也可以配置磁盤陣列,建議RAID10或者RAID6。但是如果爲了快速響應,或者多數查詢的數據量都很大,還是建議上SSD(我們就是如此)。另外,CK還支持基於配置文件的多盤存儲、冷熱數據分離和存儲策略(storage policy)設置,在特定場景下可能會很有用。我們未實操過,不多講了。
ZooKeeper
千萬要調教好ZooKeeper集羣,一旦ZK不可用,複製表和分佈式表就不可用了。ZK的數據量基本上與CK的數據量成正相關,所以一定要配置自動清理:
另外,ZK的log文件和snapshot文件建議分不同的盤存儲,儘量減少follower從leader同步的磁盤壓力,且餘量必須要留足,畢竟硬盤的成本不算高。