Snowflake:雲原生數倉的開創者
Snowflake 由甲骨文的兩位員工在 2012 年出來創辦,一開始就瞄準雲原生數倉,因此架構設計(在當時看來)非常 “激進”。超前的視野帶來超額的回報,Snowflake 在 2020 年正式上市,市值一度高達 700 億美金,創造了史上規模最大的軟件 IPO 紀錄。
本文我們綜合兩篇論文:The Snowflake Elastic Data Warehouse[1] 和 Building An Elastic Query Engine on Disaggregated Storage[2] 來大致聊聊其架構設計。
本文來自我的專欄《系統日知錄》,如果你覺得文章還不錯,歡迎訂閱支持我。
這篇文章我早就想寫了,但上次在看論文時卡住了——論文信息太多,地毯式的閱讀,很快就淹沒在細節中,當時也只看了三分之二,就擱置了。上週(20240707)在文章 Spark:如何在雲上做縮容 [3] 時提到了存算分離的 snowflake ,有讀者要求寫下,於是便重新撿起來。
相比上次 push 的方式,本次採用 pull 的方式:即不是被動的讀論文,而是先思考,如果讓我設計這麼一個雲原生數倉,我要怎麼設計,會有哪些問題等等。帶着這些問題,我再去從論文中找答案,發現效率一下高了很多,也便讓這篇文章沒有再次難產。
概述
Snowflake 主要設計目標如下:
-
存算分離:因此存儲和計算都能做到彈性伸縮,按實際用量計費
-
多租戶:保證多租戶之間的隔離性
-
高性能:在 1,2 前提下儘可能地提升性能
架構
爲了實現上述設計目標,讓我們首先看看 snowflake 的整體架構:
可以看出,snowflake 整體分爲三層。除了我們常說的存算兩層外,還有一個元信息層。
這也是分佈式數據系統經典的分法,我在這個視頻中也按照這種分類方法梳理了分佈式系統的一些脈絡,感興趣的同學可以去看看。
數據庫存儲層(Database Storage)
這一層是數據的最終持久化層,最開始時(論文發表時)通常存在各個雲廠商的對象存儲上。後來也開始支持第三方存儲,比如 Apache Iceberg[4]。
當數據寫入 snowflake 後,snowflake 會將數據組織成**微分區(micro partition)**的方式,寫入對象存儲,具體如何做優化、壓縮,如何確定大小、管理元信息,我們稍後會講。
查詢處理層(Query Processing)
也就是計算層。Snowflake 使用 **虛擬數倉(virtual warehouse,VM)**來組織計算單元,每個 VM 包含一組計算節點,可以有不同尺寸。不同 VM 之間是隔離的,不會互相影響。
查詢處理層裏有一個非常重要的緩存層,該層通常以 VM 爲單位,以一致性哈希組織(避免節點增刪後的數據 shuffle),來緩存對象存儲撈上來的數據和算子運算的中間結果。
雲服務(Cloud Services)
該層是整個 Snowflake 集羣的 “大腦”,由一組運行在雲上的計算實例構成。包含以下組件:
-
鑑權認證(authentication):多租戶
-
基礎設施管理(infra management):也就是集羣內物理信息管理管理
-
元信息管理(metadata management):主要是數據庫、表等 schema 信息的管理
-
查詢解析和優化(query parsing and optimization):SQL 解析到執行幾個階段中,除最後執行外都在這裏,畢竟這裏有全局元信息,便於優化
-
准入控制( access control)
接下來我們重點說說存儲層和計算層。
存儲層
各家雲上最便宜、容錯性最好的,無疑就是對象存儲了。雲上的系統,如果有大規模的數據存儲需求,一般都會用對象存儲。對象存儲也即 blob 存儲(不管你存的是啥,人家都當做一段不理解的二進制塊來存儲),以桶(bucket,也就是 namespace)、對象(object)兩級來進行邏輯組織。每個對象都是 path → object 的 kv 對,是拍平的,沒有類似文件系統的目錄樹結構。
對於 Snowflake 的存儲系統來說,對象存儲需要考慮的特點有:
-
自容錯:因此不用再上層進行 replication
-
不可變:因此不能進行原地更新
對象存儲本身容錯,從而使 Snowflake 無需擔心可用性問題,也就不用像傳統 share-nothing 的 TiDB 架構,得自己在多機搞多副本,然後還要用 raft 來維持一致性。
微分區(micro-partition)
在表和實際存儲中間,數據庫通常都會按 block(叫 partition 也行)對數據進行組織,傳統的數倉的 block 多爲靜態的。而 Snowflake 管每塊數據叫 micro-partition,其特點是:
-
不太大:幾十兆到幾百兆間,便於拆分、合併和遷移,也即動態分區。
-
列存:每個 micro-partition 包含一些行,但是內部爲了壓縮和應對數倉場景,是按列存儲的。
-
元信息:會保存每列 min-max 等元信息,以便進行快速過濾。
大致示意圖如下,左邊是邏輯上的一張表,右邊是物理上的數據組織,一張表 “橫切” 爲多個 micro-partition,每個 micro-partition 內 “縱切” 後按列存儲。
每個 micro-partition 作爲一個 object 存在對象存儲中。
DML 操作
由於每個 micro-partition 是不可變的,那我們往表中插入、更新和刪除行數據時該怎麼辦呢?
插入:插入最簡單,將新插入的數據生成新的 micro-partition 即可。
更新:首先找到被更新的行對應的 micro-partition ,讀到 VM 中,修改,然後整體寫回對象存儲中。這裏爲了區分新舊 micro-partition,會給每個 micro-partition 文件關聯一個版本號。
刪除:和更新類似,讀取→刪除→ 寫回,此間也要更新版本號。
基於每個文件版本號,Snowflake 可以做 MVCC 的併發控制,進一步提供 SI 級別的隔離性和時間回溯(time travel)的功能。
整體來看,就是每次一個寫事務,就會將系統的版本號推高,並且圈出一個文件集合,構成當前版本號下的一個 snapshot。基於這些 snapshot,我們可以訪問任意時間的快照,即時間回溯。
計算層
計算層也就是查詢執行層(之前的解析、優化都在雲服務層完成了),Snowflake 引入了 VM(Virtual Warehouse)的概念。下面我們梳理下幾個概念間的關係:
-
用戶:每個用戶可以起多個 VM,比如有的 VM 用來做 ETL,有的 VM 用來做查詢。
-
VM:每個 VM 可以有 XS → XXL 不同的尺寸,表示其包含不同數量、尺寸的計算資源。VM 和 VM 之間是完全隔離的。
-
節點:雲上的計算節點,會包含一定的內存和外存(HDD 或者 SSD)
在設計執行層時,一個很重要的點是,是否支持 MPP ?可以粗略的理解爲,一個查詢語句是否能在多個節點上進行類似 spark 的分佈式執行;還是隻能侷限在一個節點中進行單機執行。後者實現簡單,但是吞吐上不去。而數倉通常是大數據量場景,因此多采用前者,當然實現會更負載,因爲會在執行時,會涉及多機的的通信,以進行數據的 shuffle(可以參考 spark 中的 shuffle)。
回到 Snowflake 中,也就是一個查詢語句是否可以在 VM 中的多個節點執行,從論文中的蛛絲馬跡來看,應該是可以的。
除此之外,Snowflake 的計算引擎主要有以下幾個特點:
-
列式(columnar):由於數倉場景通常都是 “寬表窄查”,因此採用列式執行引擎效率會更高,而且可以充分利用單機緩存和 SIMD 指令加速計算。
-
向量化執行(Vectorized):這裏感覺和列式稍微有點混淆,主要是想表達算子間是 “流水線” 地執行,而非每個算子的輸出都物化。
-
推模型(push-based):也即不是用的經典的基於拉的火山模型,將控制和數據解耦,能夠充分利用緩存,效率也更高。當然,實現會更復雜一些。
伸縮
每個 VM 的大小可以按用戶的需求進行動態的伸縮。比如一個任務比較着急,就可以多加計算節點,用較短時間跑出來。由於 Snowflake 是按照 機器*時間 計費的,因此對於同一個 query 來說:多機器快速跑和少量機器慢慢跑,費用是差不多的,但前者無疑更快,利好用戶。
緩存
執行引擎用到的數據主要包括兩塊:
-
輸入數據:即執行計劃各個葉子節點需要加載的數據,需要從遠端(對象存儲)中拉取。
-
中間數據:執行計劃就是由算子組成的 DAG,每個算子會讀入數據,進行 “變換” 後,產生輸出,爲下一個算子所用。
上述兩種數據都有被複用的可能,尤其是輸入數據,即某個 table 的微分區被訪問後,後續一段時間內很有可能被再次訪問(局部性原理)。因此,可以將其緩存在 VM 節點中的內存或者外存(HDD,SDD)中。單機容量有限,因此 Snowflake 會將 VM 內所有節點的內外存組成一個緩存池,並以一致性哈希算法(可以參考這篇文章)來維護緩存,並且是 lazy 的一致性哈希,可以避免在 VM 中有節點變更時,數據頻繁的遷移的。
參考資料
[1]
The Snowflake Elastic Data Warehouse: https://dl.acm.org/doi/pdf/10.1145/2882903.2903741
[2]
Building An Elastic Query Engine on Disaggregated Storage: https://www.usenix.org/system/files/nsdi20-paper-vuppalapati.pdf
[3]
Spark:如何在雲上做縮容: https://xiaobot.net/post/93d3e9ad-90f2-47ec-a942-ff95c351cba1
[4]
Apache Iceberg: https://iceberg.apache.org/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/chgQVwK3L_o1FK7yH2rCRw