架構探索之路-第一站-clickhouse | 京東雲技術團隊

一、前言

架構, 軟件開發中最熟悉不過的名詞, 遍佈在我們的日常開發工作中, 大到項目整體, 小到功能組件, 想要實現高性能、高擴展、高可用的目標都需要優秀架構理念輔助. 所以本人嘗試編寫架構系列文章, 去剖析市面上那些經典優秀的開源項目, 學習優秀的架構理念來積累架構設計的經驗與思考, 在後續日常工作中遇到相同問題時能有更深一層的認知.

本章以實時OALP引擎Clickhouse(簡稱ck)爲例, 以其面向場景, 架構設計, 細節實現等方面來介紹, 深度瞭解其如何成爲了OLAP引擎中的性能之王.

二、Clickhouse簡介

Clickhouse是俄羅斯Yandex(俄羅斯網絡用戶最多的網站)於2016年開源的一個用於聯機分析(OLAP)的列式數據庫管理系統,採用C++語言編寫, 主要用於在線分析處理查詢, 通過SQL查詢實時生成分析數據報告.

主要面向場景是快速支持任意指標、任意維度並且可以在大數據量級下實現秒級反饋的Ad-hoc查詢(即席查詢).

三、Clickhouse架構原理

clickhouse以其卓越的性能著稱, 在相關性能對比報告中, ck在單表SQL查詢的性能是presto的2.3倍、impala的3倍、greenplum的7倍、hive的48倍. 可以看出ck在單表查詢是非常出色的, 那麼ck究竟是如何實現高效查詢的呢?

1. 引子

介紹ck查詢原理之前先以最常見的mysql爲例, 一條簡單的查詢語句是如何執行的, 然後再以ck架構師的角度去考慮ck應該如何優化. mysql查數據時會先從磁盤讀出數據所在頁(innodb存儲單元) 到內存中, 然後再從內存中返回查詢結果, 所以在我們的認知中sql查詢(排除語法詞法解析,優化等步驟)總結起來可以爲以下兩點:

  1. 磁盤讀取數據到內存
  2. 內存中解析數據匹配結果返回

在現代計算機中, CPU參與運算的時間遠小於磁盤IO的時間. 所以現代OLAP引擎大部分也選擇通過降低磁盤IO的手段來提高查詢性能, 舉例如下:

降低磁盤IO 原理 舉例 列式
分佈式 並行讀取數據,降低單節點讀取數據量 hive(texfile) 數據傾斜,網絡耗時,資源浪費
列式存儲 將每一列單獨存儲, 按需讀取 hbase 適合列使用單一的業務

2. 架構

通過以上推導分析, 我們可以得出OLAP查詢瓶頸在於磁盤IO, 那麼ck的優化手段也是借鑑了以上措施, 採用了MPP架構(大規模並行處理)+列式存儲, 擁有類似架構設計的其他數據庫產品也有很多, 爲什麼ck性能如此出衆? 接下來我們具體分析ck的核心特性, 進一步體會ck架構師的巧妙的架構理念.

2.1 列式存儲

行式存儲: 把同一行數據放到同一數據塊中, 各個數據塊之間連續存儲.

列式存儲: 把同一列數據放到同一數據塊中, 不同列之間可以分開存儲.

如同上述所講, 分析類查詢往往只需要一個表裏很少的幾個字段, Column-Store只需要讀取用戶查詢的column, 而Row-Store讀取每一條記錄的時候會把所有column的數據讀出來, 在IO上Column-Store比Row-Store效率高得多, 因此性能更好.

2.2 block

clickhouse能處理的最小單位是block, block是一羣行的集合, 默認最大爲8192行. 因爲每一列單獨存儲, 因此每個數據文件相比於行式存儲更有規律, 通過對block採用LZ4壓縮算法, 整體壓縮比大致可以8:1. 可以看出, clickhouse通過出色的壓縮比與block結構實現了批處理功能, 對比海量數據存儲下每次處理1行數據的情況, 大幅減少了IO次數, 從而達到了存儲引擎上的優化.

2.3 LSM

LSM的思想: 對數據的修改增量保持在內存中,達到指定的限制後將這些修改操作批量寫入到磁盤中,相比較於寫入操作的高性能,讀取需要合併內存中最近修改的操作和磁盤中歷史的數據,即需要先看是否在內存中,若沒有命中,還要訪問磁盤文件

LSM的原理: 把一顆大樹拆分成N棵小樹,數據先寫入內存中,隨着小樹越來越大,內存的小樹會flush到磁盤中。磁盤中的樹定期做合併操作,合併成一棵大樹,以優化讀性能。

Clickhouse通過LSM實現數據的預排序, 從而減少磁盤的讀取量. 原理就是將亂序數據通過LSM在村中排序, 然後寫入磁盤保存, 並定期合併有重合的磁盤文件. clickhouse的寫入步驟可以總結爲以下幾點:

  1. 每一批次數據寫入,先記錄日誌, 保證高可用機制
  2. 記錄日誌之後存入內存排序, 後將有序結果寫入磁盤,記錄合併次數Level=0
  3. 定期將磁盤上Level=0或1的文件合併,並標記刪除. 後續物理刪除

2.4 索引

clickhouse的採用一級索引(稀疏索引)+二級索引(跳數索引)來實現索引數據定位與查詢. 一級索引記錄每個block塊的第一個, 每次基於索引字段查詢只需要確定查詢第幾個block塊即可, 避免一個查詢遍歷所有數據. 如上述介紹,一個block塊爲8192行,那麼1億條數據只需要1萬行索引, 所以一級索引佔用存儲較小, 可常駐內存, 加速查詢. 二級索引由數據的聚合信息構建而成,根據索引類型的不同,其聚合信息的內容也不同,跳數索引的目的與一級索引一樣,也是幫助查詢時減少數據掃描的範圍, 原則都是“排除法”,即儘可能的排除那些一定不滿足條件的索引粒度

另一方面可以發現, 因ck存儲引擎按有序集合存儲, 所以在索引結構上, 並不需要再利用B+樹排序特性來定位. 所以在實際使用過程中, 也不需要滿足最左原則匹配, 只要過濾條件中包含索引列即可.

2.5 向量化執行

向量化計算(vectorization),也叫vectorized operation,也叫array programming,說的是一個事情:將多次for循環計算變成一次計算。 爲了實現向量化執行,需要利用CPU的SIMD指令。SIMD的全稱是Single Instruction Multiple Data,即用單條指令操作多條數據。現代計算機系統概念中,它是通過數據並行以提高性能的一種實現方式 ( 其他的還有指令級並行和線程級並行 ),它的原理是在CPU寄存器層面實現數據的並行操作。

在計算機系統的體系結構中,存儲系統是一種層次結構。典型服務器計算機的存儲層次結構如圖1所示。一個實用的經驗告訴我們,存儲媒介距離CPU越近,則訪問數據的速度越快。

從左至右,距離CPU越遠,則數據的訪問速度越慢。從寄存器中訪問數據的速度,是從內存訪問數據速度的300倍,是從磁盤中訪問數據速度的3000萬倍。所以利用CPU向量化執行的特性,對於程序的性能提升意義非凡。 ClickHouse目前利用SSE4.2指令集實現向量化執行。

四、Clickhouse總結

1. clickhouse的舍與得

clickhouse在追求極致性能的路上, 採取了很多優秀的設計. 如上述講的列存、批處理、預排序等等. 但是架構都有兩面性, 從一另方面也帶來了一些缺點

  • 高頻次實時寫入方面, 因ck會將批量數據直接落盤成小文件, 高頻寫入會造成大量小文件生成與合併, 影響查詢性能. 所以ck官方也是建議大批低頻的寫入, 提高寫入性能. 實際場景中建議在業務與數據庫之間引入一層數據緩存層,來實現批量寫入
  • 查詢併發問題, clickhouse是採用並行處理機制, 即一個查詢也會使用一半cpu去執行, 在安裝時會自動識別cpu核數, 所以在發揮查詢快的優勢下, 也帶來了併發能力的不足. 如果過多的查詢數堆積達到max_concurrent_queries閾值, 則會報出too many simultaneous queries異常, 這也是ck的一種限流保護機制. 所以日常使用過程中注意慢sql的排查, 併發請求的控制是保證ck高可用的關鍵.

我們瞭解其原理之後, 能夠對clickhouse有更深的認知, 也能夠解釋生產工作中曾經遇到的問題, 站在clickhouse架構師的角度去合理使用, 規避劣勢, 發揮其特性.

2. clickhouse在實際生產中遇到的問題

2.1 zookeeper高負載影響

目前clickhouse開源版本ReplicatedMergeTree引擎強依賴zookeeper完成多副本選主, 數據同步, 故障恢復等功能, zookeeper在負載較高的情況下,性能表現不佳, 甚至會出現副本無法寫入, 數據無法同步問題. 分析clickhouse對zookeeper相關的使用, 以副本複製流程爲例, ck對zookeeper頻繁的分發日誌、數據交換是引起瓶頸原因之一.

解決通用方案:

京東零售: 自研基於Raft分佈式共識算法的zookeeper替代方案.

2.2 資源管控問題

ClickHouse的資源管控能力不夠完善,在 insert、select 併發高的場景下會導致執行失敗,影響用戶體驗。這是因爲社區版ClickHouse目前僅提供依據不同用戶的最大內存控制,在超過閾值時會殺死執行的 query。

易觀性能對比: https://zhuanlan.zhihu.com/p/54907288

官網性能對比: https://clickhouse.com/

作者:京東科技 李丹楓

來源:京東雲開發者社區 轉載請註明來源

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