針對ClickHose為什么很快的問題,基于對ClickHouse的基礎概念之上,一般會回答是因為是列式存儲數據庫,同時也會說是使用了向量化引擎,所以快。上面兩方面的解釋也都能夠站得住腳,但是依然不能夠解釋真正核心的原因。因為這些技術并不是秘密,市面上很多數據庫同樣使用了這些技術,但是依然沒有ClickHouse這么快。我們可以從兩外一個角度來探討一番ClickHouse的快的秘密。
對于一般軟件設計架構的時候,一般采用自上而下的設計模式,ClickHouse的原型系統在2008年就誕生了,在誕生之初它并沒有宏偉的規劃。相反它的目的很簡單,就是希望能以最快的速度進行GROUP BY查詢和過濾,它是采用自下而上的設計方式。那么ClickHouse是如何實現自下而上的設計的呢?
1、著眼硬件,先想后做
首先從硬件功能層面著手設計,在設計之初就至少需要詳情粗如下幾個問題。
- 我們將要使用的硬件水平是怎樣的?包括CPU、內存、硬件、網絡等。
- 在這樣的硬件上,我們需要達到怎樣的性能?包括延遲、吞吐量等。
- 我們準備使用怎樣的數據結構?包括String 、HashTable、Vector等。
- 選擇的這些數據肌結構,在我們的硬件上會如何工作?
如果能想清楚上面這些問題,那么在動手實現功能之前,就已經能夠計算出粗略的性能了。所以,基于將硬件功效最大化的目的,ClickHouse會在內存中進行GROUP BY,并且使用HashTable裝載數據。與此同時,他們非常在意CPU L3級別的緩存,因為一次L3 的緩存失效會帶來70~100ns的延遲。這意味著單核CPU上,它會浪費4000萬次/秒的運算;而在一個32線程的CPU上,則可能會浪費5億次/秒的運算。所以別小看這些細節,一點一滴的將它們累加起來,數據是非常可觀的。正因為注意了這些細節,所以ClickHouse在基準查詢中能做到1.75億次/秒的數據掃描性能。
2、算法在前,抽象在后
俗話說”選擇比努力更重要。“確實,好多時候,路線選錯了再努力也是白搭。在ClickHouse的底層實現中,經常會面對一些重復的場景,例如字符串字串查詢、數組排序等。如何才能實現性能的最大化呢?算法的選擇是重中之重。clickHouse并沒有選擇字符串搜索算法書籍《Handbook of Exact String Matching Algorithms》中的35種常見的字符串搜索算法,因為這些性能不夠快。在字符串搜索方面,針對不同的場景,ClickHouse最終選擇了這些算法:對于常量,使用Volnisky算法;對于非常量,使用CPU的向量化執行SIMD,暴力優化;正則匹配使用了re2和hyperscan算法。性能是算法選擇的首要考量指標。
3 、勇于嘗鮮,不行就換
除了字符串之外,其余的場景也與它類似,ClickHouse會使用最合適、最快的算法。如果效果不錯,就保留使用;如果性能不盡人意,就將其拋棄。
4、特定場景,特殊優化
針對同一個場景的不同狀況,選擇使用不同的實現方式,盡可能將性能最大化。關于這一點,其實在前面介紹字符串查詢時,針對不同場景選擇不同算法的思路就有體現了。類似的例子還有很多,例如去重計數uniqCombined 函數,會根據數據量的不同選擇不同的算法:當數據量較小的時候,會選擇Array保存;當數據量中等的時候,會選擇HashSet;而當數據量很大的時候,則使用HyperLogLog算法。
對于數據結構比較清晰的場景,會通過代碼生成技術實現循環展開,以減少循環次數。接著就是大家熟知的大殺器—向量化執行了。SIMD被廣泛地應用于文本轉換、數據過濾、數據解壓和JSON轉換等場景。相較于單純地使用CPU,利用寄存器暴力優化也算是一種降維打擊了。
5、 持續測試,持續改進
如果只是單純地在上述細節上下功夫,還不足以構建出如此強大的ClickHouse,還需要擁有一個能夠持續驗證、持續改進的機制。由于Yandex的天然優勢,ClickHouse經常會使用真實的數據進行測試,這一點很好地保證了測試場景的真實性。與此同時,ClickHouse也是我見過的發版速度最快的開源軟件了,差不多每個月都能發布一個版本。沒有一個可靠的持續集成環境,這一點是做不到的。正因為擁有這樣的發版頻率,ClickHouse才能夠快速迭代、快速改進。
所以ClickHouse的黑魔法并不是一項單一的技術,而是一種自底向上的、追求極致性能的設計思路。這就是它如此之快的秘訣。