乾貨 | 再來聊一聊 Parquet 列式儲存格式

語言: CN / TW / HK

Parquet 是 Hadoop 生態圈中主流的列式儲存格式,最早是由 Twitter 和 Cloudera 合作開發,2015 年 5 月從 Apache 孵化器裡畢業成為 Apache 頂級專案。

圈內有這樣一句話流傳:如果說 HDFS 是大資料時代檔案系統的事實標準,Parquet 就是大資料時代儲存格式的事實標準。

整體介紹

先簡單介紹下:

  • Parquet 是一種支援巢狀結構的列式儲存格式

  • 非常適用於 OLAP 場景,按列儲存和掃描

諸如 Parquet 這種列存的特點或優勢主要體現在兩方面。

1.1 更高的壓縮比

列存使得更容易對每個列使用高效的壓縮和編碼,降低磁碟空間。(網上的case是不壓縮、gzip、snappy分別能達到11/27/19的壓縮比)

1.2 更小的IO操作

使用對映下推和謂詞下推,只讀取需要的列,跳過不滿足條件的列,能夠減少不必要的資料掃描,帶來效能的提升並在表字段比較多的時候更加明顯。

關於對映下推與謂詞下推:

對映下推,這是列式儲存最突出的優勢,是指在獲取資料時只需要掃描需要的列,不用全部掃描。

謂詞下推,是指通過將一些過濾條件儘可能的在最底層執行以減少結果集。

謂詞就是指這些過濾條件,即返回bool:

true和false的表示式,比如SQL中的大於小於等於、Like、Is Null等。

專案概述

Parquet 是與語言無關的,而且不與任何一種資料處理框架繫結在一起,適配多種語言和元件,能夠與 Parquet 適配的查詢引擎包括 Hive, Impala, Pig, Presto, Drill, Tajo, HAWQ, IBM Big SQL等,計算框架包括 MapReduce, Spark, Cascading, Crunch, Scalding, Kite 等,資料模型包括 Avro, Thrift, Protocol Buffer, POJOs 等。

Parquet 的專案組成及自下而上互動的方式如圖所示:

這裡可以將其分為三層。

  • 資料儲存層:定義 Parquet 檔案格式,其中元資料在 parquet-format 專案中定義,包括 Parquet 原始型別定義、Page型別、編碼型別、壓縮型別等等。

  • 物件轉換層:這一層在 parquet-mr 專案中,包含多個模組,作用是完成其他物件模型與 Parquet 內部資料模型的對映和轉換,Parquet 的編碼方式使用的是 striping and assembly 演算法。

  • 物件模型層:定義如何讀取 Parquet 檔案的內容,這一層轉換包括 Avro、Thrift、Protocal Buffer 等物件模型/序列化格式、Hive serde 等的適配。並且為了幫助大家理解和使用,Parquet 提供了 org.apache.parquet.example 包實現了 java 物件和 Parquet 檔案的轉換。

其中,物件模型可以簡單理解為記憶體中的資料表示,Avro, Thrift, Protocol Buffer, Pig Tuple, Hive SerDe 等這些都是物件模型。例如 parquet-mr 專案裡的 parquet-pig 專案就是負責把記憶體中的 Pig Tuple 序列化並按列儲存成 Parquet 格式,以及反過來把 Parquet 檔案的資料反序列化成 Pig Tuple。

這裡需要注意的是 Avro, Thrift, Protocol Buffer 等都有他們自己的儲存格式,但是 Parquet 並沒有使用他們,而是使用了自己在 parquet-format 專案裡定義的儲存格式。所以如果你的專案使用了 Avro 等物件模型,這些資料序列化到磁碟還是使用的 parquet-mr 定義的轉換器把他們轉換成 Parquet 自己的儲存格式。

支援巢狀的資料模型

Parquet 支援巢狀結構的資料模型,而非扁平式的資料模型,這是 Parquet 相對其他列存比如 ORC 的一大特點或優勢。支援巢狀式結構,意味著 Parquet 能夠很好的將諸如 Protobuf,thrift,json 等物件模型進行列式儲存。

Parquet 的資料模型也是 schema 表達方式,用關鍵字 message 表示。每個欄位包含三個屬性,repetition屬性(required/repeated/optional)、資料型別(primitive基本型別/group複雜型別)及欄位名。

message AddressBook {
 required string owner;
 repeated string ownerPhoneNumbers;
 repeated group contacts {
   required string name;
   optional string phoneNumber;
 }
}

這個 schema 中每條記錄表示一個人的 AddressBook。有且只有一個 owner,owner 可以有 0 個或者多個 ownerPhoneNumbers,owner 可以有 0 個或者多個 contacts。每個 contact 有且只有一個 name,這個 contact 的 phoneNumber 可有可無。這個 schema 可以用下面的樹結構來表示。

Parquet 格式的資料型別沒有複雜的 Map, List, Set 等,而是使用 repeated fields 和 groups 來表示。例如 List 和 Set 可以被表示成一個 repeated field,Map 可以表示成一個包含有 key-value 對的 repeated field,而且 key 是 required 的。

儲存模型

這裡儲存模型又可以理解為儲存格式或檔案格式,Parquet 的儲存模型主要由行組(Row Group)、列塊(Column Chuck)、頁(Page)組成。

1、行組,Row Group:Parquet 在水平方向上將資料劃分為行組,預設行組大小與 HDFS Block 塊大小對齊,Parquet 保證一個行組會被一個 Mapper 處理。

2、列塊,Column Chunk:行組中每一列儲存在一個列塊中,一個列塊具有相同的資料型別,不同的列塊可以使用不同的壓縮。

3、頁,Page:Parquet 是頁儲存方式,每一個列塊包含多個頁,一個頁是最小的編碼的單位,同一列塊的不同頁可以使用不同的編碼方式。

另外 Parquet 檔案還包含header與footer資訊,分別儲存檔案的校驗碼與Schema等資訊。參考官網的一張圖:

關於 Parquet 的儲存模型暫且瞭解到這個程度,更深入的細節可參考文末的連結。

Parquet vs ORC

除了 Parquet,另一個常見的列式儲存格式是 ORC(OptimizedRC File)。在 ORC 之前,Apache Hive 中就有一種列式儲存格式稱為 RCFile(RecordColumnar File),ORC 是對 RCFile 格式的改進,主要在壓縮編碼、查詢效能方面做了優化。因此 ORC/RC 都源於 Hive,主要用來提高 Hive 查詢速度和降低 Hadoop 的資料儲存空間。

Parquet 與 ORC 的不同點總結以下:

  • 巢狀結構支援:Parquet 能夠很完美的支援巢狀式結構,而在這一點上 ORC 支援的並不好,表達起來複雜且效能和空間都損耗較大。

  • 更新與 ACID 支援:ORC 格式支援 update 操作與 ACID,而 Parquet 並不支援。

  • 壓縮與查詢效能:在壓縮空間與查詢效能方面,Parquet 與 ORC 總體上相差不大。可能 ORC 要稍好於 Parquet。

  • 查詢引擎支援:這方面 Parquet 可能更有優勢,支援 Hive、Impala、Presto 等各種查詢引擎,而 ORC 與 Hive 接觸的比較緊密,而與 Impala 適配的並不好。之前我們說 Impala 不支援 ORC,直到 CDH 6.1.x 版本也就是 Impala3.x 才開始以 experimental feature 支援 ORC 格式。

關於 Parquet 與 ORC,首先建議根據實際情況進行選擇。另外,根據筆者的綜合評估,如果不是一定要使用 ORC 的特性,還是建議選擇 Parquet。

Parquet 工具

最後介紹下社群的一個 Parquet 開源工具,主要用於檢視 Parquet 檔案元資料、Schema 等。

使用方法:

#Runfrom Hadoop
hadoop jar ./parquet-tools-<VERSION>.jar --help
hadoop jar ./parquet-tools-<VERSION>.jar <command> my_parquet_file.parq
#Runlocally
java -jar ./parquet-tools-<VERSION>.jar --help
java -jar ./parquet-tools-<VERSION>.jar <command> my_parquet_file.parq

比如:

$ hadoop jar parquet-tools-1.8.0.jar schema 20200515160701.parquet               
message t_staff_info_partition {

  optional int64 age;
  optional binary dt (UTF8);
  optional int64 id;
  optional binary name (UTF8);
  optional binary updated_time (UTF8);
}

parquet tools工具可在maven repo下載: 

http://mvnrepository.com/artifact/org.apache.parquet/parquet-tools