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. 多租戶:保證多租戶之間的隔離性

  3. 高性能:在 1,2 前提下儘可能地提升性能

架構

爲了實現上述設計目標,讓我們首先看看 snowflake 的整體架構:

snow architecture

可以看出,snowflake 整體分爲三層。除了我們常說的存算兩層外,還有一個元信息層。

這也是分佈式數據系統經典的分法,我在這個視頻中也按照這種分類方法梳理了分佈式系統的一些脈絡,感興趣的同學可以去看看。

數據庫存儲層(Database Storage)

這一層是數據的最終持久化層,最開始時(論文發表時)通常存在各個雲廠商的對象存儲上。後來也開始支持第三方存儲,比如 Apache Iceberg[4]。

當數據寫入 snowflake 後,snowflake 會將數據組織成**微分區(micro partition)**的方式,寫入對象存儲,具體如何做優化、壓縮,如何確定大小、管理元信息,我們稍後會講。

查詢處理層(Query Processing)

也就是計算層。Snowflake 使用 **虛擬數倉(virtual warehouse,VM)**來組織計算單元,每個 VM 包含一組計算節點,可以有不同尺寸。不同 VM 之間是隔離的,不會互相影響。

查詢處理層裏有一個非常重要的緩存層,該層通常以 VM 爲單位,以一致性哈希組織(避免節點增刪後的數據 shuffle),來緩存對象存儲撈上來的數據和算子運算的中間結果。

雲服務(Cloud Services)

該層是整個 Snowflake 集羣的 “大腦”,由一組運行在雲上的計算實例構成。包含以下組件:

  1. 鑑權認證(authentication):多租戶

  2. 基礎設施管理(infra management):也就是集羣內物理信息管理管理

  3. 元信息管理(metadata management):主要是數據庫、表等 schema 信息的管理

  4. 查詢解析和優化(query parsing and optimization):SQL 解析到執行幾個階段中,除最後執行外都在這裏,畢竟這裏有全局元信息,便於優化

  5. 准入控制( access control)

接下來我們重點說說存儲層和計算層。

存儲層

各家雲上最便宜、容錯性最好的,無疑就是對象存儲了。雲上的系統,如果有大規模的數據存儲需求,一般都會用對象存儲。對象存儲也即 blob 存儲(不管你存的是啥,人家都當做一段不理解的二進制塊來存儲),以桶(bucket,也就是 namespace)、對象(object)兩級來進行邏輯組織。每個對象都是 path → object 的 kv 對,是拍平的,沒有類似文件系統的目錄樹結構。

對於 Snowflake 的存儲系統來說,對象存儲需要考慮的特點有:

  1. 自容錯:因此不用再上層進行 replication

  2. 不可變:因此不能進行原地更新

對象存儲本身容錯,從而使 Snowflake 無需擔心可用性問題,也就不用像傳統 share-nothing 的 TiDB 架構,得自己在多機搞多副本,然後還要用 raft 來維持一致性。

微分區(micro-partition)

在表和實際存儲中間,數據庫通常都會按 block(叫 partition 也行)對數據進行組織,傳統的數倉的 block 多爲靜態的。而 Snowflake 管每塊數據叫 micro-partition,其特點是:

  1. 不太大:幾十兆到幾百兆間,便於拆分、合併和遷移,也即動態分區。

  2. 列存:每個 micro-partition 包含一些行,但是內部爲了壓縮和應對數倉場景,是按列存儲的。

  3. 元信息:會保存每列 min-max 等元信息,以便進行快速過濾。

大致示意圖如下,左邊是邏輯上的一張表,右邊是物理上的數據組織,一張表 “橫切” 爲多個 micro-partition,每個  micro-partition 內 “縱切” 後按列存儲。

snowflake storage layout

每個 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)的概念。下面我們梳理下幾個概念間的關係:

  1. 用戶:每個用戶可以起多個 VM,比如有的 VM 用來做 ETL,有的 VM 用來做查詢。

  2. VM:每個 VM 可以有 XS → XXL 不同的尺寸,表示其包含不同數量、尺寸的計算資源。VM 和 VM 之間是完全隔離的。

  3. 節點:雲上的計算節點,會包含一定的內存和外存(HDD 或者 SSD)

在設計執行層時,一個很重要的點是,是否支持 MPP ?可以粗略的理解爲,一個查詢語句是否能在多個節點上進行類似 spark 的分佈式執行;還是隻能侷限在一個節點中進行單機執行。後者實現簡單,但是吞吐上不去。而數倉通常是大數據量場景,因此多采用前者,當然實現會更負載,因爲會在執行時,會涉及多機的的通信,以進行數據的 shuffle(可以參考 spark 中的 shuffle)。

回到 Snowflake 中,也就是一個查詢語句是否可以在 VM 中的多個節點執行,從論文中的蛛絲馬跡來看,應該是可以的。

除此之外,Snowflake 的計算引擎主要有以下幾個特點:

  1. 列式(columnar):由於數倉場景通常都是 “寬表窄查”,因此採用列式執行引擎效率會更高,而且可以充分利用單機緩存和 SIMD 指令加速計算。

  2. 向量化執行(Vectorized):這裏感覺和列式稍微有點混淆,主要是想表達算子間是 “流水線” 地執行,而非每個算子的輸出都物化。

  3. 推模型(push-based):也即不是用的經典的基於拉的火山模型,將控制和數據解耦,能夠充分利用緩存,效率也更高。當然,實現會更復雜一些。

伸縮

每個 VM 的大小可以按用戶的需求進行動態的伸縮。比如一個任務比較着急,就可以多加計算節點,用較短時間跑出來。由於 Snowflake 是按照 機器*時間 計費的,因此對於同一個 query 來說:多機器快速跑和少量機器慢慢跑,費用是差不多的,但前者無疑更快,利好用戶。

緩存

執行引擎用到的數據主要包括兩塊:

  1. 輸入數據:即執行計劃各個葉子節點需要加載的數據,需要從遠端(對象存儲)中拉取。

  2. 中間數據:執行計劃就是由算子組成的 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