天天看點

Parquet 學習筆記

2010年 google 發表了一篇論文《Dremel: Interactive Analysis of Web-Scale Datasets》,介紹了其 Dermel 系統是如何利用列式存儲管理嵌套資料的,嵌套資料就是層次資料,如定義一個班級,班級由同學組成,同學的資訊有學号、年齡、身高等。

Parquet 是 Dremel 的開源實作,作為一種列式存儲檔案格式,2015年稱為 Apache 頂級項目,後來被 Spark 項目吸收,作為 Spark 的預設資料源,在不指定讀取和存儲格式時,預設讀寫 Parquet 格式的檔案。

今天不介紹嵌套資料是如何映射到每一列了,簡單來說就是把不同層級的屬性拍到一級,類似降維打擊。這樣,一個嵌套資料可以看成獨立的多個屬性,每一個屬性就是一列,和表結構差不多。

雖然是按列存儲,但資料是一行一行來的,那什麼時候将記憶體中的資料寫檔案呢?我們知道檔案隻能順序寫,假如每收到一行資料就寫入磁盤,那就是行式存儲了。

一個解決方案是為每個列開一個檔案,假如資料有 n 個屬性,就需要 n 個檔案,每次寫資料就需要追加到 n 個檔案中。但是對于檔案格式來說,使用者肯定希望把複雜的資料存到一個檔案中,而不希望管理一堆小檔案(可以想象你做了一個ppt,每一頁存成了一個檔案),是以一個 Parquet 檔案中必須存儲資料的所有屬性。

另一個解決方案是在記憶體中緩存一些資料,等緩存到一定量後,将各個列的資料放在一起打包,這樣各個包就可以按一定順序寫到一個檔案中。這就是列式存儲的精髓:按列緩存打包。

按照上邊這種方式,Parquet 在每一列内也需要分成一個個的資料包,這個資料包就叫 Page,Page 的分割标準可以按資料點數(如每1000行資料打成一個 Page),也可以按空間占用(如每列的資料攢到8KB合成一個 Page)。

一個 Page 的資料就是一列,類型相同,在存儲到磁盤之前一般都會進行編碼壓縮,為了快速查詢、也為了解壓縮這一個 Page,在寫的時候先統計一下最大最小值,叫做 PageHeader,存儲在 Page 的開頭,其實就是 Page 的 中繼資料(metadata)。PageHeader 後邊就是資料了,讀取一個 Page 時,可以先通過 PageHeader 進行過濾。

Parquet 又把多個 Page 放在一起存儲,叫 Column Chunk。于是,每一列都由多個 Column Chunk 組成,并且也有其對應的 ColumnChunk Metadata。注意,這隻是一個完整資料的一個屬性,一個資料的多個屬性要放在多個 Column Chunk 的,這多個 Column Chunk 放在一起就叫做一個 Row Group。

下邊這就是 Parquet 官方介紹:

Parquet 學習筆記

magic number 就類似水印,最後有整個檔案的 Metadata。還是看圖吧,Parquet 的官方檔案格式圖是下面這樣的:

Parquet 學習筆記

左邊是資料,右邊是 File Metadata。如果覺得太複雜了,可以看我畫的簡潔版:

Parquet 學習筆記

是不是清爽很多!File Metadata 中有對應的 Row Group Metadata,裡面還有 Column Chunk Metadta,和資料的組織形式類似,就不展開畫了。

Parquet 的接口就不介紹了,有興趣的去吧:

https://github.com/apache/parquet-format

列式存儲檔案格式到底有多列,取決于每列在記憶體中緩存的資料量,由于同一列的各個 Page 互相獨立,如果每個 Page 隻緩存一個資料點,就退化成行式存儲了(比行式存儲還差)。是以,列式存儲有一個需要注意的就是列不能太多,這是個大坑。

繼續閱讀