Parquet的那些事(一)

數據的接入、處理、存儲與查詢,是大數據系統不可或缺的四個環節。隨着數據量的增加,大家開始尋找一種高效的數據格式,來解決存儲與查詢環節的痛點。

  • 高效的壓縮編碼,用於降低存儲成本
  • 高效的讀取能力,用於支撐快速查詢

Parquet便是在這樣的背景下誕生,與TEXT、JSON、CSV等文件格式相比,它有三個核心特徵,爲解決上述的痛點問題提供了基礎。

  • 列式存儲
  • 自帶Schema
  • 具備Predicate Filter特性

在行式存儲中,一行的多列是連續的寫在一起的,而在列式存儲中,數據按列分開存儲。由於同一列的數據類型是一樣的,可以使用更高效的壓縮編碼進一步節約存儲空間。

對於大多數數據存儲服務,如MySQL、MongoDB、Elasticsearch等,爲了提高查詢性能,都會在數據寫入時建立相應的索引。而存放在HDFS、AWS S3上的大數據是直接以文件形式存儲的,那麼如何實現快速查詢呢?目前主要有三種手段,核心目的是儘可能只加載有符合數據的文件,而這些手段都能基於Parquet實現。

  • Partition Pruning。類似於將文件分文件夾存放的思路,根據某些字段將數據進行分區,在查詢時指定相應的分區條件。
  • Column Projection。在查詢中指定需要返回的字段,跳過不必要的字段,減少需要加載的數據量。
  • Predicate Filter。先判斷一個文件中是否存在符合條件的數據,有則加載相應的數據,否則跳過。

本文主要介紹Parquet的文件結構、Predicate Filter特性以及常用的一些工具。與Parquet相似且有着廣泛應用的還有ORC,我們將在後面介紹二者的區別。本文所述主要基於parquet-mr 1.8.2。

Parquet文件結構

一個Parquet文件的內容由Header、Data Block和Footer三部分組成。在文件的首尾各有一個內容爲PAR1的Magic Number,用於標識這個文件爲Parquet文件。Header部分就是開頭的Magic Number。
Data Block是具體存放數據的區域,由多個Row Group組成,每個Row Group包含了一批數據。比如,假設一個文件有1000行數據,按照相應大小切分成了兩個Row Group,每個擁有500行數據。每個Row Group中,數據按列彙集存放,每列的所有數據組合成一個Column Chunk。因此一個Row Group由多個Column Chunk組成,Column Chunk的個數等於列數。每個Column Chunk中,數據按照Page爲最小單元來存儲,根據內容分爲Data Page和Dictionary Page。這樣逐層設計的目的在於:

  • 多個Row Group可以實現數據的並行加載
  • 不同Column Chunk用來實現列存儲
  • 進一步分割成Page,可以實現更細粒度的數據訪問

Footer部分由File Metadata、Footer Length和Magic Number三部分組成。Footer Length是一個4字節的數據,用於標識Footer部分的大小,幫助找到Footer的起始指針位置。Magic Number同樣是PAR1。File Metada包含了非常重要的信息,包括Schema和每個Row Group的Metadata。每個Row Group的Metadata又由各個Column的Metadata組成,每個Column Metadata包含了其Encoding、Offset、Statistic信息等等。

Predicate Pushdown Filter特性

所謂Predicate Pushdown Filter,是指在不影響結果的情況下,將過濾條件提前執行,過濾掉不滿足條件的文件,降低需要傳輸的數據集,從而提升性能。比如,在S3上面有1000個文件(100GB),現在要執行下面的SQL查詢,有兩種選擇:

  • 將所有文件內容都加載進來(1000個文件),再對內容執行過濾條件,得到結果
  • 只加載有符合條件(age >= 22)的數據的文件(100個文件),得到結果
SELECT name, school FROM student WHERE age >= 22

第二個選擇就是Predicate Pushdown Filter的方式。那麼在Parquet中如何做到這點呢?在讀取Parquet文件時,會先根據Footer Length找到Footer起始位置,讀取Parquet中的Metadata,通過Metadata中的信息可以幫助我們進行相應的條件過濾。目前有兩種實現,分別針對不同的數據類型。

  • 整型數據
    Column Metada中,有該Column Chunk中數據的Range信息: Max和Min。將過濾條件與Range信息進行對比,就可以知道是否需要加載該文件的數據。
File 0
	Row Group 0, Column Statistics -> (Min -> 20, Max -> 30)
	Row Group 1, Column Statistics -> (Min -> 10, Max -> 20)
File 1
	Row Group 0, Column Statistics -> (Min -> 6, Max -> 21)
	Row Group 1, Column Statistics -> (Min -> 25, Max -> 45)
	
通過對比過濾條件age >= 22,只需要加載File 0的Row Group 0和File 1的Row Group 1中的數據。
  • 字符數據
    先說說什麼是字典編碼。假設有個字段name,在10條數據中的值分別爲:
name:
	bruce, cake, bruce, kevin, bruce, kevin, cake, leo, cake, bruce

我們可以對其編碼,將其變爲:

name:
	0, 1, 0, 2, 0, 2, 1, 3, 1, 0
dictionary:
	0 -> bruce, 1 -> cake, 2 -> kevin, 3 -> leo

這種方式在很多開源軟件中都有使用,比如Elasticsearch,有兩個優點:

  • 可以節省存儲空間
  • 可以根據dictionary中的內容,過濾掉不符合條件的數據

在Parquet中,我們可以根據字符編碼的特性來做相應的過濾。通過Column Metada中的信息,讀取相應的Dictionary Page進行對比,從而過濾掉不符合條件的數據。

查詢語句: SELECT name, school FROM student WHERE name = "leo"

File 0
	Row Group 0, Column 0 -> 0: bruce, 1:cake
	Row Group 1, Column 0 -> 0: bruce, 2:kevin
File 1
	Row Group 0, Column 0 -> 0: bruce, 1:cake, 2: kevin
	Row Group 1, Column 0 -> 0: bruce, 1:cake, 3: leo
	
通過對比過濾條件name = "leo",只需要加載File 1的Row Group 1中的數據。

常見的Parquet工具

與JSON、CSV等文件相比,Parquet是無法人類可讀的,需要通過一些工具來窺探其內容,這裏列舉一些常用的工具,供選擇。

  • parquet-mr提供的工具parquet-tools
    • 官方提供,下載源碼編譯jar包
    • 採用命令行形式,通過參數來指定相關功能
示例:
java -jar ./parquet-tools-1.8.2.jar meta part-00003-c04d37ba-3de5-4f7b-addc-b6f4bc5a7ab1-c000.snappy.parquet
  • 開源的工具bigdata-file-viewer
    • 通過GitHub下載jar包
    • 有UI可以查看數據,通過命令行啓動UI
java -jar ./BigdataFileViewer-1.2-SNAPSHOT-jar-with-dependencies.jar


(全文完,本文地址:https://bruce.blog.csdn.net/article/details/104582229
(版權聲明:本人拒絕不規範轉載,所有轉載需徵得本人同意,並且不得更改文字與圖片內容。大家相互尊重,謝謝!)

Bruce
2020/03/09 凌晨

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章