為什么 OLAP需要列式存儲
為什么這么設計(Why’s THE Design)是一系列關于計算機領域中程序設計決策的文章,我們在這個系列的每一篇文章中都會提出一個具體的問題并從不同的角度討論這種設計的優缺點、對具體實現造成的影響。如果你有想要了解的問題,可以在文章下面留言。
ClickHouse 是最近比較熱門的用于在線分析處理的(OLAP)[^1]數據存儲,與我們常見的 MySQL、PostgreSQL 等傳統的關系型數據庫相比,ClickHouse、Hive 和 HBase 等用于在線分析處理(OLAP)場景的數據存儲往往都會使用列式存儲。
olap-oltp-databases
圖 1 - OLAP 和 OLTP
對數據庫稍有了解的讀者都知道,在線事務處理(Online Transaction Processing、OLTP)[^2]和在線分析處理(Online Analytical Processing、OLAP)是數據庫最常見的兩種場景,這兩種場景不是唯二的兩種,從中衍生出來的還有混合事務分析處理(Hybrid Transactional/Analytical Processing、HTAP)[^3]等概念。
在線事務處理是最常見的場景,在線服務需要為用戶實時提供服務,提供服務的過程中可能要查詢或者創建一些記錄;而在線分析處理的場景需要批量處理用戶數據,數據分析師會根據用戶產生的數據分析用戶行為和畫像、產出報表和模型。
標題中提到的列式存儲與傳統關系型數據庫的行式存儲相對應,如下圖所示,其中行式存儲以數據行或者實體為邏輯單元管理數據,數據行的存儲都是連續的,而列式存儲以數據列為邏輯單元管理數據,相鄰的數據都是具有相同類型的數據。
圖 2 - 行式存儲和列式存儲
既然我們已經了解了標題中提到的兩個概念:OLAP 和列式存儲,那么接下來將從以下兩個方面分析為什么列式存儲更適合 OLAP 的場景。
- 列式存儲可以滿足快速讀取特定列的需求,在線分析處理往往需要在上百列的寬表中讀取指定列分析;
- 列式存儲就近存儲同一列的數據,使用壓縮算法可以得到更高的壓縮率,減少存儲占用的磁盤空間;
按需讀取
在線服務需要應對用戶發起的增刪改查需求,雖然查詢的需求往往都是寫入請求的幾倍、甚至幾十倍,但是寫操作帶來的負責一致性問題成為了在線服務數據存儲不得不解決的問題,MySQL 和 PostgreSQL 等使用關系型數據庫提供的事務可以提供很好的方案。
正是因為 OLTP 場景中大多數的操作都是以記錄作為單位的,所以將經常被同時使用的數據相鄰存儲也是很符合邏輯的,但是如果我們將 MySQL 等數據庫用于 OLAP 場景,最常見的查詢也可能需要遍歷整張表中的全部數據。
圖 3 - 在行式存儲獲取特定列
如上圖所示,當我們僅需要獲取上表中年齡的分布時,也仍然需要讀取表中的全部數據并在內存中丟棄不需要的數據行,其中黃色部分都是我們不關心的數據,這浪費了大量的 I/O 和內存資源。雖然我們可以使用輔助索引解決這些問題,但是對于 OLAP 中常見的幾十列甚至上百列的寬表就捉襟見肘了。
列式存儲會按列存儲數據,這也意味著在讀取數據表中的特定列時,我們只需要找到相應內存空間的起始位置,然后讀取這片連續的內存空間就可以獲得關心的全部數據。
圖 4 - 在列式存儲獲取特定列
哪怕在幾百列的大表中找到幾個特定列也不需要遍歷整張表,只需要找到列的起始位置就可以快速獲取相關的數據,減少了 I/O 和內存資源的浪費,這也是為什么面向列的存儲系統更適合在 OLAP 的場景中使用。
數據壓縮
因為列式存儲將同一列的數據存儲在一起,所以使用壓縮算法可以得到更高的壓縮率,減少存儲占用的磁盤空間。壓縮算法的基本原理其實很簡單,它使用基于特定規則的數據表示原數據,如下所示的字符串中包含連續的相同字符,我們使用最符合直覺的壓縮算法就可以減少字符串的長度:
圖 5 - 簡單的壓縮算法
上圖中所有的黃色方塊表示前面字符串的重復次數,這種簡單的壓縮策略可以在保證無損的情況下將字符串的長度壓縮 33%,然而壓縮率是由壓縮算法和數據的特性共同決定的。與面向行的數據存儲相比,面向列的數據存儲會將相同類型的數據就近存儲,這也給壓縮算法的提供了更多發揮的空間。
雖然壓縮算法實際上是一種使用 CPU 時間換取 I/O 時間和空間的策略,但是在多數情況下,這種生意都是穩賺不賠的。壓縮算法通過減少數據的大小、減少磁盤的尋道時間提高 I/O 的性能、減少數據的傳輸時間并提高緩沖區的命中率,節省的 I/O 時間可以輕易補償它帶來的 CPU 額外開銷[^4]。
總結
在線分析處理的場景雖然一直都存在,不過隨著數字化浪潮的演進,我們也只是在最近才采集到了海量的用戶數據。因為過去的系統無法滿足今天海量數據的分析和處理需求,所以才出現了為細分場景設計的系統,面向列的存儲系統也因為它的以下特性在 OLAP 的場景中煥發了光彩:
- 列式存儲可以滿足快速讀取特定列的需求,在線分析處理往往需要在上百列的寬表中讀取指定列分析,而傳統的行式存儲在分析數據時往往需要使用索引或者遍歷整張表,帶來了非常大的額外開銷;
- 列式存儲就近存儲同一列的數據,使用壓縮算法可以得到更高的壓縮率,減少存儲占用的磁盤空間,雖然帶來了 CPU 時間的額外開銷,但是節省的 I/O 時間比帶來的額外開銷更多;
列式存儲在 OLAP 的場景中有著種種優勢,不過它也不是數據存儲中的銀彈,仍然有很多缺點,不過在這里就不做討論了。到最后,我們還是來看一些比較開放的相關問題,有興趣的讀者可以仔細思考一下下面的問題:
- 列式存儲在 OLTP 的場景中有哪些優點?
- HTAP 的場景會使用哪種方式存儲數據?
如果對文章中的內容有疑問或者想要了解更多軟件工程上一些設計決策背后的原因,可以在博客下面留言,作者會及時回復本文相關的疑問并選擇其中合適的主題作為后續的內容。
本文轉載自微信公眾號「真沒什么邏輯」,可以通過以下二維碼關注。轉載本文請聯系真沒什么邏輯公眾號。