clickhouse-(07)-性能優化

數據類型

  • 儘量用數值型
    • 建表時能用數值型或日期時間型表示的字段,就不要用字符串——全String類型在以Hive爲中心的數倉建設中常見,但CK環境不應受此影響。
  • 直接用DataTime
    • 直接用DateTime表示時間列,而不是用整形的時間戳。因爲CK中DateTime的底層就是時間戳,效率高,可讀性好,且轉換函數豐富。
  • 不用Nullable
    • 官方已經指出Nullable類型幾乎總是會拖累性能,因爲存儲Nullable列時需要創建一個額外的文件來存儲NULL的標記,並且Nullable列無法被索引。因此除非極特殊情況,應直接使用字段默認值表示空,或者自行指定一個在業務中無意義的值(例如用-1表示沒有商品ID)。

分區和索引

  • 事實表必須分區,分區粒度根據業務特點決定,不宜過粗或過細。我們當前都是按天分區,按小時、周、月分區也比較常見(系統表中的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的數據量成正相關,所以一定要配置自動清理:

 
autopurge.purgeInterval = 1 autopurge.snapRetainCount = 5
 
 

另外,ZK的log文件和snapshot文件建議分不同的盤存儲,儘量減少follower從leader同步的磁盤壓力,且餘量必須要留足,畢竟硬盤的成本不算高。

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