乾貨 | 再來聊一聊 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下載: 

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