Apache Parquet設(shè)計(jì)解讀

官網(wǎng)地址:https://parquet.apache.org/docs
編碼:https://www.waitingforcode.com/apache-parquet/encodings-apache-parquet/read
Nested類型編碼參考文章:Dremel: interactive analysis of web-scale datasets
Nested類型編碼參考解釋:https://github.com/julienledem/redelm/wiki/The-striping-and-assembly-algorithms-from-the-Dremel-paper

1. Overview

Parquet是Hadoop生態(tài)里面一個(gè)比較流行的列存儲(chǔ)格式。它存在的意義就是最大化利用壓縮的列存儲(chǔ)表示霎终。
當(dāng)然支持各種壓縮和編碼模式:

  • 可以細(xì)化到列級(jí)別堡掏,即每一列用不同算法壓縮暇赤;
  • 甚至支持未來的蛾娶,尚未發(fā)明的壓縮技術(shù)盗冷。

注意荔烧,總的來說兑徘,Parquet是一個(gè)非常底層的文件存儲(chǔ)技術(shù)刚盈,它和任何數(shù)據(jù)處理技術(shù)、軟件都是正交的挂脑,這些技術(shù)也都可以用Parquet來存文件藕漱。

Parquet is built from the ground up with complex nested data structures in mind, and uses the [record shredding and assembly algorithm] described in the Dremel paper. We believe this approach is superior to simple flattening of nested name spaces

2. Concepts

  • 塊:就是hdfs block
  • 文件:就是hdfs文件,元數(shù)據(jù)存于master中崭闲。
  • 行組(Row group):邏輯上數(shù)據(jù)表中的一組行肋联。由于實(shí)際上都是列存儲(chǔ),所以在hadoop上找不到所謂的行組刁俭,這只是一個(gè)邏輯上的概念橄仍。
  • 列切片(column chunk):某一列中連續(xù)的一片數(shù)據(jù)。其實(shí)是行組的一部分牍戚,但是它是物理存在的侮繁,在文件中連續(xù)存儲(chǔ)的。
  • 頁(page):頁時(shí)列切片的一部分如孝。概念上是說不可分的一個(gè)單元宪哩,是針對(duì)于壓縮編碼來說的。一個(gè)列切片里面可能有幾個(gè)不同類型的頁分別壓縮第晰。

總結(jié)一下這個(gè)概念锁孟,一個(gè)文件可能邏輯上包含若干行組彬祖,每個(gè)行組中,每個(gè)列構(gòu)成一個(gè)列切片品抽,列切片內(nèi)由多個(gè)頁構(gòu)成涧至,頁是最小壓縮單元。

編者繪制了下圖幫助概念理解桑包。


image.png

在并行化級(jí)別方面:

  • MapReduce是并行文件,也可能是并行行組纺非;
  • 系統(tǒng)IO其實(shí)是在并行列切片哑了;
  • 壓縮針對(duì)的則是頁。

3. File Format

一個(gè)文件用Parquet來存烧颖,基本格式如下圖:

  • 首尾的魔數(shù)是系統(tǒng)相關(guān)的弱左,不細(xì)說;
  • 文件主體是M個(gè)行組炕淮,N個(gè)列的存儲(chǔ)拆火;
    • 比如最開始存第一個(gè)行組,把第一個(gè)行組的N個(gè)列切片涂圆,全部存好们镜,然后進(jìn)入到第二個(gè)行組;
  • 文件的最后是整個(gè)文件的元數(shù)據(jù)润歉,包含各個(gè)行組的位置模狭,各個(gè)列切片的位置和一些其它統(tǒng)計(jì)信息。
    • 整個(gè)文件的元數(shù)據(jù)放在了文件最后踩衩,這是為了寫入時(shí)能順序?qū)懸槐榫屯旯ぁ?/li>
  • 具體存放的時(shí)候嚼鹉,尾部的元數(shù)據(jù)可以單獨(dú)存一個(gè)文件,數(shù)據(jù)文件可以分成幾個(gè)Parquet files來存放驱富;每個(gè)Parquet file可以包含一個(gè)或幾個(gè)行組锚赤。
  • 數(shù)據(jù)文件中每個(gè)行組中的每個(gè)頁會(huì)有一個(gè)頁元數(shù)據(jù),頁有三種褐鸥,數(shù)據(jù)頁线脚,字典頁,索引頁晶疼。字典頁每個(gè)列切片最多一個(gè)酒贬,是該列值的編碼字典(編碼部分會(huì)講到);索引頁是一個(gè)高級(jí)功能翠霍。
  • 讀取的時(shí)候锭吨,首先讀取元數(shù)據(jù),找到自己想要列切片的位置寒匙,然后順序訪問即可零如。
  • Parquet可以保證躏将,對(duì)于每個(gè)RowGroup只會(huì)被一個(gè)mapper處理。


    image.png

3.1 Configurations

對(duì)于Parquet考蕾,有兩個(gè)重要的參數(shù)配置:

  • Row Group Size:行組大小
    • 行組比較大祸憋,順序掃描時(shí)就可以有更多的順序磁盤訪問;
    • 行組過大肖卧,寫入時(shí)需要的內(nèi)存buffer也會(huì)比較大蚯窥。
    • 綜合來說,還是會(huì)選擇比較大塞帐,推薦1GB大小拦赠。
    • 由于一整個(gè)行組可能需要讀出來,所以最好是一個(gè)Row Group塞進(jìn)一個(gè)HDFS block里面葵姥,所以block也推薦1GB大小荷鼠。
  • Page Size:頁大小
    • 頁大的話,壓縮率高榔幸,訪問也略快允乐;
    • 頁小的話,對(duì)于細(xì)粒度的查詢就可以訪問更少的數(shù)據(jù)削咆;
    • 綜合一下牍疏,推薦8KB。

3.2 Metadata

元數(shù)據(jù)有三個(gè)級(jí)別拨齐,文件元數(shù)據(jù)麸澜,列(chunk)元數(shù)據(jù)职员,頁元數(shù)據(jù)灰嫉。

3.3 Types

類型支持的原則是盡量少奇适,因?yàn)镻arquet一般不直接面向用戶号胚,這里只關(guān)注類型如何在磁盤中存儲(chǔ)永高。更復(fù)雜的類型椒舵,比如String春畔,可以用BYTE_ARRAY進(jìn)行支持狈邑,再額外加一個(gè)注解表名如何解釋這個(gè)BYTE_ARRAY即可蹂匹。這樣只用實(shí)現(xiàn)很少量幾種類型的代碼碘菜,就可以表示多種用戶類型。


image.png

3.4 Nested Encoding

這里是說層次化類型的編碼限寞。舉個(gè)例子:
如下圖:想json,xml等類型一樣忍啸,數(shù)據(jù)文件中的列也可能是嵌套復(fù)合類型。這些類型不僅僅是列表履植,map這么簡單计雌,而是組合類型的不斷組合,理論上有無限深度的玫霎。

  • 在下圖這個(gè)例子里凿滤,repeated其實(shí)就相當(dāng)于列表(單一類型)妈橄,group就相當(dāng)于一個(gè)字典類型(key是固定的,類似C里面的Struct)翁脆。
  • 具體到存儲(chǔ)上眷蚓,共有6個(gè)列:
    • DocId;
    • Links.Backward;
    • Links.Forwad;
    • Name.Language.Code;
    • Name.Language.Country;
    • Name.Url;
image.png

要處理這樣類型的列,就需要先展平反番,編碼沙热,用的時(shí)候子再解碼恢復(fù)結(jié)構(gòu)查詢。
下面三個(gè)小節(jié)罢缸,分別解決這個(gè)過程中的三個(gè)問題:

  1. 層次化記錄的無損表示校读;
  2. 列的快速編碼;
  3. 高效解碼重組祖能。

3.4.1 Repetition and De?nition Levels

在一個(gè)列式存儲(chǔ)文件中,只給定某兩個(gè)值蛾洛,我們是沒法確定它們是來自一條record還是兩條記錄的养铸,因?yàn)榇嬖谥貜?fù)類型的這個(gè)概念(如上圖Name列,其實(shí)可以理解為列表類型)轧膘。為了得以區(qū)分钞螟,定義了重復(fù)級(jí)別和定義級(jí)別兩個(gè)概念。

  • 舉一個(gè)例子谎碍,對(duì)于Name.Language.Code鳞滨,存下來其實(shí)就是連續(xù)的一組字符串了,我們需要知道每一個(gè)Code蟆淀,它屬于哪一個(gè)Language (1個(gè)Name有多個(gè)Language)拯啦,屬于哪一個(gè)Name (1條記錄可能有多個(gè)Name)。

3.4.1.1 repetition levels

  • 重復(fù)級(jí)別 (r) 是說當(dāng)前這個(gè)值熔任,是屬于一個(gè)新紀(jì)錄(r=0)褒链?或者在同一個(gè)記錄的第幾層 (r=n)?

  • 舉個(gè)例子疑苔,比如剛才Name.Language.Code這一列甫匹,路徑上有Name, Language兩個(gè)可重復(fù)對(duì)象(列表),所以r的值在【0,2】惦费。- - 以r1為例兵迅,en-us這個(gè)值開啟一個(gè)新對(duì)象,它的r是0,薪贫;en是在Name.Language這個(gè)級(jí)別的重復(fù)(開啟了一個(gè)新的Name.Language)恍箭,r值就是2; en-gb是在Name這個(gè)級(jí)別的重復(fù)(開啟了一個(gè)新的Name),r值是1瞧省。


    image.png
  • 重復(fù)級(jí)別解決的是可重復(fù)類型的問題季惯,如果列的路徑上根本沒有可重復(fù)類型(repeated)吠各,那就不需要定義重復(fù)級(jí)別。

  • 仍然有一個(gè)問題是勉抓,雖然我們定義清楚了級(jí)別贾漏,但是沒定義清楚位置。比如剛才的en-gb它到底屬于第二個(gè)name還是第三個(gè)name無法推測(cè)藕筋,因此要在en-gb之前加一個(gè)null纵散。

3.4.1.2 de?nition levels

定義級(jí)別是給所有null值的一個(gè)屬性,把位置定義清楚隐圾。定義級(jí)別d描述的是null是在哪一級(jí)別的缺省伍掀。

  • 舉Name.Language.Country為例子:

    • 第一個(gè)null, d=2, 描述的是在一個(gè)新的Name.Language中沒有country
    • 第二個(gè)null, d=1,描述的是在一個(gè)新的Name中沒有Language暇藏,自然也沒有country
    • 第三個(gè)null蜜笤,d=1,也是一個(gè)新的Name中沒有Language,即r2盐碱。
  • 對(duì)于非null值把兔,就賦予正常的嵌套深度。

  • 定義級(jí)別解決的是可選類型的問題瓮顽,如果一列的路徑上所有類型都是必須的(required)县好,那就沒必要定義定義級(jí)別。

3.4.1.3 encoding

最終的編碼是非常緊湊的暖混。每個(gè)列由多個(gè)block組成缕贡,每個(gè)block有兩種levels,有具體的數(shù)值拣播。這些levels也不是全都按序存儲(chǔ)晾咪,主要是按需存儲(chǔ),bit能省則省贮配,一些隱含關(guān)系都被挖掘出來禀酱。

3.4.2 Splitting Records into Columns

本節(jié)聚焦于如何將原始數(shù)據(jù)都覆成列格式:


image.png

3.4.3 Record Assembly

通過一個(gè)有限自動(dòng)機(jī)把需要的數(shù)據(jù)組合起來。
【編者:我自己也沒看進(jìn)去這部分內(nèi)容牧嫉,有興趣的朋友可以參考論文和代碼再研究下剂跟。】


image.png

3.5 Data Pages

數(shù)據(jù)頁中酣藻,包含定義級(jí)別曹洽,重復(fù)級(jí)別,和編碼后的數(shù)據(jù)值辽剧。如果數(shù)據(jù)全都是Required送淆,并且不嵌套,那就不需要這兩個(gè)級(jí)別的信息怕轿,而只有編碼后的數(shù)據(jù)值偷崩。

3.5.1 Encoding

3.5.1.1 Plain

支持所有類型辟拷,最簡單的存儲(chǔ)方式。

  • BOOLEAN: Bit Packed, LSB first
  • INT32: 4 bytes little endian
  • INT64: 8 bytes little endian
  • INT96: 12 bytes little endian (deprecated)
  • FLOAT: 4 bytes IEEE little endian
  • DOUBLE: 8 bytes IEEE little endian
  • BYTE_ARRAY: length in 4 bytes little endian followed by the bytes contained in the array
  • FIXED_LEN_BYTE_ARRAY: the bytes contained in the array

3.5.1.2 Dictionary Encoding

  • 把所有值建字典阐斜,key是值衫冻,value是id。
  • 之前我們說到數(shù)據(jù)頁可能會(huì)配備一個(gè)字典頁谒出,存的就是這個(gè)字典隅俘;
  • 這樣數(shù)據(jù)頁中的每一項(xiàng)數(shù)據(jù),就可以用一個(gè)整數(shù)id來存儲(chǔ)笤喳,最大位寬看基數(shù)而定为居。
  • 具體來說,數(shù)據(jù)頁頭部第一個(gè)字節(jié)描述每個(gè)值用多寬的bit來存杀狡。后面的各個(gè)值就是這些id蒙畴。一般這些id也會(huì)進(jìn)一步用RLE編碼。
  • 當(dāng)字典條目太多呜象,或者字典太大膳凝,會(huì)自動(dòng)退回plain。
image.png

3.5.1.3 Run Length Encoding / Bit-Packing Hybrid

  • RLE的思路下圖即可解釋:重復(fù)次數(shù)+重復(fù)值董朝。


    image.png
  • bit-packed基本意思是,一個(gè)int要32bit干跛,但很多時(shí)候用不上32bit子姜,可能10bit就夠了(對(duì)于512以內(nèi)的數(shù)據(jù)),那么就可以縮短寬度存儲(chǔ)楼入。
  • 這二者混合使用哥捕,取決于值字符的重復(fù)程度選擇其中一個(gè),可以進(jìn)一步壓縮數(shù)據(jù)頁的這些值嘉熊。


    image.png

3.5.1.4 Bit-packed (Deprecated)

廢棄的bit-packed非常簡單遥赚,就是每個(gè)類型固定寬度,然后按順序存儲(chǔ)就可以了阐肤。但注意是bit級(jí)別的凫佛,粒度是很細(xì)的。因?yàn)樗阅芤话阍邢В耆蝗鏡LE或者和RLE混合愧薛,目前只是為了兼容性而存在。
用于:

  • 對(duì)定義級(jí)別和重復(fù)級(jí)別的編碼衫画。

3.5.1.5 Delta Encoding

支持INT32, INT64毫炉,和字節(jié)數(shù)組類型。

  • 其基本思想是削罩,數(shù)據(jù)的差異一般可能很小瞄勾,如果只存差異的話费奸,就不需要那么長的bit。這尤其適用于時(shí)間进陡、日期愿阐、有公共前綴的字符串。
  • 數(shù)據(jù)是分塊的四濒,一旦有過大差異换况,可以通過調(diào)整分塊來改善。每個(gè)分塊都有自己的數(shù)據(jù)寬度盗蟆。


    image.png

3.5.1.6 Delta-length byte array

專門用于字節(jié)數(shù)組戈二。

  • 數(shù)組長度單獨(dú)提出來用Delta來壓縮;
  • 后面跟著純字節(jié)喳资,也利于后續(xù)壓縮算法觉吭。


    image.png

3.5.1.7 Byte Stream Split

用于浮點(diǎn)數(shù)。
把每一位單獨(dú)抽出來仆邓,雖然總體大小沒有減少鲜滩,但是利于后續(xù)壓縮算法壓縮。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末节值,一起剝皮案震驚了整個(gè)濱河市徙硅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搞疗,老刑警劉巖嗓蘑,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異匿乃,居然都是意外死亡桩皿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門幢炸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泄隔,“玉大人,你說我怎么就攤上這事宛徊》疰遥” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵闸天,是天一觀的道長巷燥。 經(jīng)常有香客問我,道長号枕,這世上最難降的妖魔是什么缰揪? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上钝腺,老公的妹妹穿的比我還像新娘抛姑。我一直安慰自己,他們只是感情好艳狐,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布定硝。 她就那樣靜靜地躺著,像睡著了一般毫目。 火紅的嫁衣襯著肌膚如雪蔬啡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天镀虐,我揣著相機(jī)與錄音箱蟆,去河邊找鬼。 笑死刮便,一個(gè)胖子當(dāng)著我的面吹牛空猜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恨旱,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼辈毯,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了搜贤?” 一聲冷哼從身側(cè)響起谆沃,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仪芒,沒想到半個(gè)月后唁影,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桌硫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年夭咬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啃炸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铆隘。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖南用,靈堂內(nèi)的尸體忽然破棺而出膀钠,到底是詐尸還是另有隱情,我是刑警寧澤裹虫,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布肿嘲,位于F島的核電站,受9級(jí)特大地震影響筑公,放射性物質(zhì)發(fā)生泄漏雳窟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一匣屡、第九天 我趴在偏房一處隱蔽的房頂上張望封救。 院中可真熱鬧拇涤,春花似錦、人聲如沸誉结。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惩坑。三九已至掉盅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間以舒,已是汗流浹背趾痘。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稀轨,地道東北人扼脐。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像奋刽,于是被迫代替她去往敵國和親瓦侮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容