Parquet調(diào)研報(bào)告
1. 概述
1.1 簡(jiǎn)介
Apache Parquet是Hadoop生態(tài)圈中一種新型列式存儲(chǔ)格式国旷,它可以兼容Hadoop生態(tài)圈中大多數(shù)計(jì)算框架(Hadoop、Spark等)井氢,被多種查詢(xún)引擎支持(Hive、Impala、Drill等),并且它是語(yǔ)言和平臺(tái)無(wú)關(guān)的弄跌。Parquet最初是由Twitter和Cloudera(由于Impala的緣故)合作開(kāi)發(fā)完成并開(kāi)源,2015年5月從Apache的孵化器里畢業(yè)成為Apache頂級(jí)項(xiàng)目尝苇,最新的版本是1.8.1铛只。
Parquet是語(yǔ)言無(wú)關(guān)的,而且不與任何一種數(shù)據(jù)處理框架綁定在一起糠溜,適配多種語(yǔ)言和組件淳玩,能夠與Parquet配合的組件有:
查詢(xún)引擎: Hive, Impala, Pig, Presto, Drill, Tajo, HAWQ, IBM Big SQL
計(jì)算框架: MapReduce, Spark, Cascading, Crunch, Scalding, Kite
數(shù)據(jù)模型: Avro, Thrift, Protocol Buffers, POJOs
那么Parquet是如何與這些組件協(xié)作的呢?這個(gè)可以通過(guò)下圖來(lái)說(shuō)明非竿。數(shù)據(jù)從內(nèi)存到Parquet文件或者反過(guò)來(lái)的過(guò)程主要由以下三個(gè)部分組成:
- 存儲(chǔ)格式(storage format)
parquet-format項(xiàng)目定義了Parquet內(nèi)部的數(shù)據(jù)類(lèi)型蜕着、存儲(chǔ)格式等。
- 對(duì)象模型轉(zhuǎn)換器(object model converters)
這部分功能由parquet-mr項(xiàng)目來(lái)實(shí)現(xiàn)红柱,主要完成外部對(duì)象模型與Parquet內(nèi)部數(shù)據(jù)類(lèi)型的映射承匣。
- 對(duì)象模型(object models)
對(duì)象模型可以簡(jiǎn)單理解為內(nèi)存中的數(shù)據(jù)表示,Avro, Thrift, Protocol Buffers, Hive SerDe, Pig Tuple, Spark SQL InternalRow等這些都是對(duì)象模型锤悄。Parquet也提供了一個(gè)example object model 幫助大家理解韧骗。
例如parquet-mr項(xiàng)目里的parquet-pig項(xiàng)目就是負(fù)責(zé)把內(nèi)存中的Pig Tuple序列化并按列存儲(chǔ)成Parquet格式,以及反過(guò)來(lái)把Parquet文件的數(shù)據(jù)反序列化成Pig Tuple铁蹈。
這里需要注意的是Avro, Thrift, Protocol Buffers都有他們自己的存儲(chǔ)格式宽闲,但是Parquet并沒(méi)有使用他們众眨,而是使用了自己在parquet-format項(xiàng)目里定義的存儲(chǔ)格式握牧。所以如果你的應(yīng)用使用了Avro等對(duì)象模型容诬,這些數(shù)據(jù)序列化到磁盤(pán)還是使用的parquet-mr定義的轉(zhuǎn)換器把他們轉(zhuǎn)換成Parquet自己的存儲(chǔ)格式。
1.2 列式存儲(chǔ)
列式存儲(chǔ)沿腰,顧名思義就是按照列進(jìn)行存儲(chǔ)數(shù)據(jù)览徒,把某一列的數(shù)據(jù)連續(xù)的存儲(chǔ),每一行中的不同列的值離散分布颂龙。列式存儲(chǔ)技術(shù)并不新鮮习蓬,在關(guān)系數(shù)據(jù)庫(kù)中都已經(jīng)在使用,尤其是在針對(duì)OLAP場(chǎng)景下的數(shù)據(jù)存儲(chǔ)措嵌,由于OLAP場(chǎng)景下的數(shù)據(jù)大部分情況下都是批量導(dǎo)入躲叼,基本上不需要支持單條記錄的增刪改操作,而查詢(xún)的時(shí)候大多數(shù)都是只使用部分列進(jìn)行過(guò)濾企巢、聚合枫慷,對(duì)少數(shù)列進(jìn)行計(jì)算(基本不需要select * from xx之類(lèi)的查詢(xún))。
example:
以下這張表有A浪规、B或听、C三個(gè)字段:
A | B | C |
---|---|---|
A1 | B1 | C1 |
A2 | B2 | C2 |
A3 | B3 | C3 |
行存儲(chǔ):
A1 | B1 | C1 | A2 | B2 | C2 | A3 | B3 | C3 |
---|
列存儲(chǔ)
A1 | A2 | A3 | B1 | B2 | B3 | C1 | C2 | C3 |
---|
列式存儲(chǔ)可以大大提升這類(lèi)查詢(xún)的性能,較之于行是存儲(chǔ)笋婿,列式存儲(chǔ)能夠帶來(lái)這些優(yōu)化:
- 查詢(xún)的時(shí)候不需要掃描全部的數(shù)據(jù)誉裆,而只需要讀取每次查詢(xún)涉及的列,這樣可以將I/O消耗降低N倍缸濒,另外可以保存每一列的統(tǒng)計(jì)信息(min足丢、max、sum等)庇配,實(shí)現(xiàn)部分的謂詞下推斩跌。
- 由于每一列的成員都是同構(gòu)的,可以針對(duì)不同的數(shù)據(jù)類(lèi)型使用更高效的數(shù)據(jù)壓縮算法讨永,進(jìn)一步減小I/O滔驶。
- 由于每一列的成員的同構(gòu)性,可以使用更加適合CPU pipeline的編碼方式卿闹,減小CPU的緩存失效揭糕。
2. Parquet詳解
2.1 數(shù)據(jù)模型
理解Parquet首先要理解這個(gè)列存儲(chǔ)格式的數(shù)據(jù)模型。我們以一個(gè)下面這樣的schema和數(shù)據(jù)為例來(lái)說(shuō)明這個(gè)問(wèn)題锻霎。
message AddressBook {
required string owner;
repeated string ownerPhoneNumbers;
repeated group contacts {
required string name;
optional string phoneNumber;
}
}
這個(gè)schema中每條記錄表示一個(gè)人的AddressBook著角。有且只有一個(gè)owner,owner可以有0個(gè)或者多個(gè)ownerPhoneNumbers旋恼,owner可以有0個(gè)或者多個(gè)contacts吏口。每個(gè)contact有且只有一個(gè)name,這個(gè)contact的phoneNumber可有可無(wú)。
每個(gè)schema的結(jié)構(gòu)是這樣的:根叫做message产徊,message包含多個(gè)fields昂勒。每個(gè)field包含三個(gè)屬性:repetition, type, name。repetition可以是以下三種:required(出現(xiàn)1次)舟铜,optional(出現(xiàn)0次或者1次)戈盈,repeated(出現(xiàn)0次或者多次)。type可以是一個(gè)group或者一個(gè)primitive類(lèi)型谆刨。
Parquet格式的數(shù)據(jù)類(lèi)型不需要復(fù)雜的Map, List, Set等塘娶,而是使用repeated fields 和 groups來(lái)表示。例如List和Set可以被表示成一個(gè)repeated field痊夭,Map可以表示成一個(gè)包含有key-value 對(duì)的repeated group
刁岸,而且key是required的。
List(或Set)可以用repeated field來(lái)表示:
Map可以用包含key-value對(duì)且key是required的repeated group來(lái)表示:
2.2 列存儲(chǔ)格式
列存儲(chǔ)通過(guò)將相同基本類(lèi)型(primitive type)的值存儲(chǔ)在一起來(lái)提供高效的編碼和解碼她我。為了用列存儲(chǔ)來(lái)存儲(chǔ)如上嵌套的數(shù)據(jù)結(jié)構(gòu)虹曙,我們需要將該schema用某種方式映射到一系列的列使我們能夠?qū)⒂涗泴?xiě)到列中并且能讀取成原來(lái)的嵌套的數(shù)據(jù)結(jié)構(gòu)。
在Parquet格式的存儲(chǔ)中鸦难,一個(gè)schema的樹(shù)結(jié)構(gòu)有幾個(gè)葉子節(jié)點(diǎn)(葉子節(jié)點(diǎn)都是primitive type)根吁,實(shí)際的存儲(chǔ)中就會(huì)有多少column。
上面的schema的樹(shù)結(jié)構(gòu)如圖所示:
上面這個(gè)schema的數(shù)據(jù)存儲(chǔ)實(shí)際上有四個(gè)column合蔽,如下圖所示:
只有字段值不能表達(dá)清楚記錄的結(jié)構(gòu)击敌。給出一個(gè)repeated field的兩個(gè)值,我們不知道此值是按什么‘深度’被重復(fù)的(比如拴事,這些值是來(lái)自?xún)蓚€(gè)不同的記錄沃斤,還是相同的記錄中兩個(gè)重復(fù)的值)。同樣的刃宵,給出一個(gè)缺失的可選字段衡瓶,我們不知道整個(gè)路徑有多少字段被顯示定義了。因此我們將介紹repetition level 和 definition level的概念牲证。
example:
兩條嵌套的記錄和它們的schema:
將上圖的兩條記錄用列存儲(chǔ)表示:
上面的例子主要是想讓大家對(duì)嵌套結(jié)構(gòu)的列式存儲(chǔ)有個(gè)直觀的印象哮针,包括repetition level 和 definition level的應(yīng)用,接下來(lái)詳細(xì)介紹repetition level 和 definition level坦袍。
2.3 Definition levels
Definition level指明該列的路徑上多少個(gè)可選field被定義了十厢。
嵌套數(shù)據(jù)類(lèi)型的特點(diǎn)是有些field(optional field 和 repeated field)可以是空的,也就是沒(méi)有定義捂齐。如果一個(gè)field是定義的蛮放,那么它的所有的父節(jié)點(diǎn)都是被定義的。從根節(jié)點(diǎn)開(kāi)始遍歷奠宜,當(dāng)某一個(gè)field的路徑上的節(jié)點(diǎn)開(kāi)始是空的時(shí)候我們記錄下當(dāng)前的深度作為這個(gè)field的Definition Level包颁。如果一個(gè)field的definition Level等于這個(gè)field的最大definition Level就說(shuō)明這個(gè)field是有數(shù)據(jù)的瞻想。對(duì)于required類(lèi)型的field必須是有定義的,所以這個(gè)Definition Level是不需要的娩嚼。在關(guān)系型數(shù)據(jù)中蘑险,optional類(lèi)型的field被編碼成0表示空和1表示非空(或者反之)。
注:definition Level是該路徑上有定義的repeated field 和 optional field的個(gè)數(shù)待锈,不包括required field漠其,因?yàn)閞equired field是必須有定義的嘴高。
再舉個(gè)簡(jiǎn)單的例子:
message ExampleDefinitionLevel {
optional group a {
required group b {
optional string c;
}
}
}
因?yàn)閎是required field竿音,所以第3行c的definition level為1而不是2(因?yàn)閎是required field,所有不需計(jì)算在內(nèi))拴驮;第4行c的definition level為2而不是3(理由同上).
2.4 Repetition levels
Repetition level指明該值在路徑中哪個(gè)repeated field重復(fù)春瞬。
Repetition level是針對(duì)repeted field的。注意在圖2中的Code字段套啤】砥可以看到它在r1出現(xiàn)了3次∏甭伲‘en-us’萄涯、‘en’在第一個(gè)Name中,而‘en-gb’在第三個(gè)Name中唆鸡。結(jié)合了圖2你肯定能理解我上一句話并知道‘en-us’涝影、‘en’、‘en-gb’出現(xiàn)在r1中的具體位置争占,但是不看圖的話呢燃逻?怎么用文字,或者說(shuō)是一種定義臂痕、一種屬性伯襟、一個(gè)數(shù)值,詮釋清楚它們出現(xiàn)的位置握童?這就是重復(fù)深度這個(gè)概念的作用姆怪,它能用一個(gè)數(shù)字告訴我們?cè)诼窂街械氖裁粗貜?fù)字段,此值重復(fù)了澡绩,以此來(lái)確定此值的位置(注意稽揭,這里的重復(fù),特指在某個(gè)repeated類(lèi)型的字段下“重復(fù)”出現(xiàn)的“重復(fù)”)英古。我們用深度0表示一個(gè)紀(jì)錄的開(kāi)頭(虛擬的根節(jié)點(diǎn))淀衣,深度的計(jì)算忽略非重復(fù)字段(標(biāo)簽不是repeated的字段都不算在深度里)。所以在Name.Language.Code這個(gè)路徑中召调,包含兩個(gè)重復(fù)字段膨桥,Name和Language蛮浑,如果在Name處重復(fù),重復(fù)深度為1(虛擬的根節(jié)點(diǎn)是0只嚣,下一級(jí)就是1)沮稚,在Language處重復(fù)就是2,不可能在Code處重復(fù)册舞,它是required類(lèi)型蕴掏,表示有且僅有一個(gè);同樣的调鲸,在路徑Links.Forward中盛杰,Links是optional的,不參與深度計(jì)算(不可能重復(fù))藐石,F(xiàn)orward是repeated的即供,因此只有在Forward處重復(fù)時(shí)重復(fù)深度為1。現(xiàn)在我們從上至下掃描紀(jì)錄r1于微。當(dāng)我們遇到’en-us’逗嫡,我們沒(méi)看到任何重復(fù)字段,也就是說(shuō)株依,重復(fù)深度是0驱证。當(dāng)我們遇到‘en’,字段Language重復(fù)了(在‘en-us’的路徑里已經(jīng)出現(xiàn)過(guò)一個(gè)Language)恋腕,所以重復(fù)深度是2.最終抹锄,當(dāng)我們遇到’en-gb‘,Name重復(fù)了(Name在前面‘en-us’和‘en’的路徑里已經(jīng)出現(xiàn)過(guò)一次吗坚,而此Name后Language只出現(xiàn)過(guò)一次祈远,沒(méi)有重復(fù)),所以重復(fù)深度是1商源。因此车份,r1中Code的值的重復(fù)深度是0、2牡彻、1.
要注意第二個(gè)Name在r1中沒(méi)有包含任何Code值扫沼。為了確定‘en-gb’出現(xiàn)在第三個(gè)Name而不是第二個(gè),我們添加一個(gè)NULL值在‘en’和‘en-gb’之間(如圖3所示)庄吼。
2.5 Striping and assembly
下面用AddressBook的例子來(lái)說(shuō)明Striping和assembly的過(guò)程缎除。
對(duì)于每個(gè)column的最大的Repetion Level和 Definition Level下圖所示。
下面這樣兩條record:
AddressBook {
owner: "Julien Le Dem",
ownerPhoneNumbers: "555 123 4567",
ownerPhoneNumbers: "555 666 1337",
contacts: {
name: "Dmitriy Ryaboy",
phoneNumber: "555 987 6543",
},
contacts: {
name: "Chris Aniszczyk"
}
}
AddressBook {
owner: "A. Nonymous"
}
以contacts.phoneNumber這一列為例总寻,"555 987 6543"這個(gè)contacts.phoneNumber的Definition Level是最大Definition Level=2器罐。而如果一個(gè)contact沒(méi)有phoneNumber,那么它的Definition Level就是1渐行。如果連contact都沒(méi)有轰坊,那么它的Definition Level就是0铸董。
下面我們拿掉其他三個(gè)column只看contacts.phoneNumber這個(gè)column,把上面的兩條record簡(jiǎn)化成下面的樣子:
AddressBook {
contacts: {
phoneNumber: "555 987 6543"
}
contacts: {
}
}
AddressBook {
}
這兩條記錄的序列化過(guò)程如下圖所示:
如果我們要把這個(gè)column寫(xiě)到磁盤(pán)上肴沫,磁盤(pán)上會(huì)寫(xiě)入這樣的數(shù)據(jù):
注意:NULL實(shí)際上不會(huì)被存儲(chǔ)粟害,如果一個(gè)column value的Definition Level小于該column最大Definition Level的話,那么就表示這是一個(gè)空值颤芬。
下面是從磁盤(pán)上讀取數(shù)據(jù)并反序列化成AddressBook對(duì)象的過(guò)程:
-
讀取第一個(gè)三元組R=0, D=2, Value=”555 987 6543”
R=0 表示是一個(gè)新的record悲幅,要根據(jù)schema創(chuàng)建一個(gè)新的nested record直到Definition Level=2。
D=2 說(shuō)明Definition Level=Max Definition Level站蝠,那么這個(gè)Value就是contacts.phoneNumber這一列的值汰具,賦值操作contacts.phoneNumber=”555 987 6543”。
-
讀取第二個(gè)三元組 R=1, D=1
R=1 表示不是一個(gè)新的record沉衣,是上一個(gè)record中一個(gè)新的contacts郁副。
D=1 表示contacts定義了,但是contacts的下一個(gè)級(jí)別也就是phoneNumber沒(méi)有被定義豌习,所以創(chuàng)建一個(gè)空的contacts。
-
讀取第三個(gè)三元組 R=0, D=0
R=0 表示一個(gè)新的record拔疚,根據(jù)schema創(chuàng)建一個(gè)新的nested record直到Definition Level=0肥隆,也就是創(chuàng)建一個(gè)AddressBook根節(jié)點(diǎn)。
可以看出在Parquet列式存儲(chǔ)中稚失,對(duì)于一個(gè)schema的所有葉子節(jié)點(diǎn)會(huì)被當(dāng)成column存儲(chǔ)栋艳,而且葉子節(jié)點(diǎn)一定是primitive類(lèi)型的數(shù)據(jù)。對(duì)于這樣一個(gè)primitive類(lèi)型的數(shù)據(jù)會(huì)衍生出三個(gè)sub columns (R, D, Value)句各,也就是從邏輯上看除了數(shù)據(jù)本身以外會(huì)存儲(chǔ)大量的Definition Level和Repetition Level吸占。那么這些Definition Level和Repetition Level是否會(huì)帶來(lái)額外的存儲(chǔ)開(kāi)銷(xiāo)呢?實(shí)際上這部分額外的存儲(chǔ)開(kāi)銷(xiāo)是可以忽略的凿宾。因?yàn)閷?duì)于一個(gè)schema來(lái)說(shuō)level都是有上限的矾屯,而且非repeated類(lèi)型的field不需要Repetition Level,required類(lèi)型的field不需要Definition Level初厚,也可以縮短這個(gè)上限件蚕。例如對(duì)于Twitter的7層嵌套的schema來(lái)說(shuō),只需要3個(gè)bits就可以表示這兩個(gè)Level了产禾。
對(duì)于存儲(chǔ)關(guān)系型的record排作,record中的元素都是非空的(NOT NULL in SQL)。Repetion Level和Definition Level都是0亚情,所以這兩個(gè)sub column就完全不需要存儲(chǔ)了妄痪。所以在存儲(chǔ)非嵌套類(lèi)型的時(shí)候,Parquet格式也是一樣高效的楞件。
2.6 文件格式
-
行組(Row Group):按照行將數(shù)據(jù)物理上劃分為多個(gè)單元衫生,每一個(gè)行組包含一定的行數(shù)僧著。一個(gè)行組包含這個(gè)行組對(duì)應(yīng)的區(qū)間內(nèi)的所有列的列塊。
官方建議:
更大的行組意味著更大的列塊障簿,使得能夠做更大的序列IO盹愚。我們建議設(shè)置更大的行組(512MB-1GB)。因?yàn)橐淮慰赡苄枰x取整個(gè)行組站故,所以我們想讓一個(gè)行組剛好在一個(gè)HDFS塊中皆怕。因此,HDFS塊的大小也需要被設(shè)得更大西篓。一個(gè)最優(yōu)的讀設(shè)置是:1GB的行組愈腾,1GB的HDFS塊,1個(gè)HDFS塊放一個(gè)HDFS文件岂津。
列塊(Column Chunk):在一個(gè)行組中每一列保存在一個(gè)列塊中虱黄,行組中的所有列連續(xù)的存儲(chǔ)在這個(gè)行組文件中。不同的列塊可能使用不同的算法進(jìn)行壓縮吮成。一個(gè)列塊由多個(gè)頁(yè)組成橱乱。
頁(yè)(Page):每一個(gè)列塊劃分為多個(gè)頁(yè),頁(yè)是壓縮和編碼的單元粱甫,對(duì)數(shù)據(jù)模型來(lái)說(shuō)頁(yè)是透明的泳叠。在同一個(gè)列塊的不同頁(yè)可能使用不同的編碼方式。官方建議一個(gè)頁(yè)為8KB茶宵。
上圖展示了一個(gè)Parquet文件的結(jié)構(gòu)危纫,一個(gè)文件中可以存儲(chǔ)多個(gè)行組,文件的首位都是該文件的Magic Code乌庶,用于校驗(yàn)它是否是一個(gè)Parquet文件种蝶,F(xiàn)ooter length存儲(chǔ)了文件元數(shù)據(jù)的大小,通過(guò)該值和文件長(zhǎng)度可以計(jì)算出元數(shù)據(jù)的偏移量瞒大,文件的元數(shù)據(jù)中包括每一個(gè)行組的元數(shù)據(jù)信息和當(dāng)前文件的Schema信息螃征。除了文件中每一個(gè)行組的元數(shù)據(jù),每一頁(yè)的開(kāi)始都會(huì)存儲(chǔ)該頁(yè)的元數(shù)據(jù)糠赦,在Parquet中会傲,有三種類(lèi)型的頁(yè):數(shù)據(jù)頁(yè)、字典頁(yè)和索引頁(yè)拙泽。數(shù)據(jù)頁(yè)用于存儲(chǔ)當(dāng)前行組中該列的值淌山,字典頁(yè)存儲(chǔ)該列值的編碼字典,每一個(gè)列塊中最多包含一個(gè)字典頁(yè)顾瞻,索引頁(yè)用來(lái)存儲(chǔ)當(dāng)前行組下該列的索引泼疑,目前Parquet中還不支持索引頁(yè),但是在后面的版本中增加荷荤。
2.7 映射下推(Project PushDown)
說(shuō)到列式存儲(chǔ)的優(yōu)勢(shì)退渗,映射下推是最突出的移稳,它意味著在獲取表中原始數(shù)據(jù)時(shí)只需要掃描查詢(xún)中需要的列,由于每一列的所有值都是連續(xù)存儲(chǔ)的会油,所以分區(qū)取出每一列的所有值就可以實(shí)現(xiàn)TableScan算子个粱,而避免掃描整個(gè)表文件內(nèi)容。
在Parquet中原生就支持映射下推翻翩,執(zhí)行查詢(xún)的時(shí)候可以通過(guò)Configuration傳遞需要讀取的列的信息都许,這些列必須是Schema的子集,映射每次會(huì)掃描一個(gè)Row Group的數(shù)據(jù)嫂冻,然后一次性得將該Row Group里所有需要的列的Cloumn Chunk都讀取到內(nèi)存中胶征,每次讀取一個(gè)Row Group的數(shù)據(jù)能夠大大降低隨機(jī)讀的次數(shù),除此之外桨仿,Parquet在讀取的時(shí)候會(huì)考慮列是否連續(xù)睛低,如果某些需要的列是存儲(chǔ)位置是連續(xù)的,那么一次讀操作就可以把多個(gè)列的數(shù)據(jù)讀取到內(nèi)存服傍。
2.8 謂詞下推(Predicate PushDown)
在數(shù)據(jù)庫(kù)之類(lèi)的查詢(xún)系統(tǒng)中最常用的優(yōu)化手段就是謂詞下推了钱雷,通過(guò)將一些過(guò)濾條件盡可能的在最底層執(zhí)行可以減少每一層交互的數(shù)據(jù)量,從而提升性能伴嗡,例如”select count(1) from A Join B on A.id = B.id where A.a > 10 and B.b < 100″SQL查詢(xún)中急波,在處理Join操作之前需要首先對(duì)A和B執(zhí)行TableScan操作,然后再進(jìn)行Join瘪校,再執(zhí)行過(guò)濾,最后計(jì)算聚合函數(shù)返回名段,但是如果把過(guò)濾條件A.a > 10和B.b < 100分別移到A表的TableScan和B表的TableScan的時(shí)候執(zhí)行阱扬,可以大大降低Join操作的輸入數(shù)據(jù)。
無(wú)論是行式存儲(chǔ)還是列式存儲(chǔ)伸辟,都可以在將過(guò)濾條件在讀取一條記錄之后執(zhí)行以判斷該記錄是否需要返回給調(diào)用者麻惶,在Parquet做了更進(jìn)一步的優(yōu)化,優(yōu)化的方法時(shí)對(duì)每一個(gè)Row Group的每一個(gè)Column Chunk在存儲(chǔ)的時(shí)候都計(jì)算對(duì)應(yīng)的統(tǒng)計(jì)信息信夫,包括該Column Chunk的最大值窃蹋、最小值和空值個(gè)數(shù)。通過(guò)這些統(tǒng)計(jì)值和該列的過(guò)濾條件可以判斷該Row Group是否需要掃描静稻。另外Parquet未來(lái)還會(huì)增加諸如Bloom Filter和Index等優(yōu)化數(shù)據(jù)警没,更加有效的完成謂詞下推。
3. 性能
3.1 壓縮
上圖是展示了使用不同格式存儲(chǔ)TPC-H和TPC-DS數(shù)據(jù)集中兩個(gè)表數(shù)據(jù)的文件大小對(duì)比振湾,可以看出Parquet較之于其他的二進(jìn)制文件存儲(chǔ)格式能夠更有效的利用存儲(chǔ)空間杀迹,而新版本的Parquet(2.0版本)使用了更加高效的頁(yè)存儲(chǔ)方式,進(jìn)一步的提升存儲(chǔ)空間押搪。
3.2 查詢(xún)
上圖展示了Twitter在Impala中使用不同格式文件執(zhí)行TPC-DS基準(zhǔn)測(cè)試的結(jié)果树酪,測(cè)試結(jié)果可以看出Parquet較之于其他的行式存儲(chǔ)格式有較明顯的性能提升浅碾。
上圖展示了criteo公司在Hive中使用ORC和Parquet兩種列式存儲(chǔ)格式執(zhí)行TPC-DS基準(zhǔn)測(cè)試的結(jié)果,測(cè)試結(jié)果可以看出在數(shù)據(jù)存儲(chǔ)方面续语,兩種存儲(chǔ)格式在都是用snappy壓縮的情況下量中存儲(chǔ)格式占用的空間相差并不大垂谢,查詢(xún)的結(jié)果顯示Parquet格式稍好于ORC格式,兩者在功能上也都有優(yōu)缺點(diǎn)疮茄,Parquet原生支持嵌套式數(shù)據(jù)結(jié)構(gòu)滥朱,而ORC對(duì)此支持的較差,這種復(fù)雜的Schema查詢(xún)也相對(duì)較差;而Parquet不支持?jǐn)?shù)據(jù)的修改和ACID娃豹,但是ORC對(duì)此提供支持焚虱,但是在OLAP環(huán)境下很少會(huì)對(duì)單條數(shù)據(jù)修改,更多的則是批量導(dǎo)入懂版。
4. 總結(jié)
本文介紹了一種支持嵌套數(shù)據(jù)模型對(duì)的列式存儲(chǔ)格式Parquet鹃栽,作為大數(shù)據(jù)系統(tǒng)中OLAP查詢(xún)的優(yōu)化方案,它已經(jīng)被多種查詢(xún)引擎原生支持躯畴,并且部分高性能引擎將其作為默認(rèn)的文件存儲(chǔ)格式民鼓。通過(guò)數(shù)據(jù)編碼和壓縮,以及映射下推和謂詞下推功能蓬抄,Parquet的性能也較之其它文件格式有所提升丰嘉,可以預(yù)見(jiàn),隨著數(shù)據(jù)模型的豐富和Ad hoc查詢(xún)的需求嚷缭,Parquet將會(huì)被更廣泛的使用饮亏。
5. 參考
- Google論文:Dremel: Interactive Analysis of Web-Scale Datasets
- Twitter博文:Dremel made simple with Parquet
- http://www.importnew.com/2617.html
- http://www.2cto.com/database/201605/509506.html
- http://www.infoq.com/cn/articles/in-depth-analysis-of-parquet-column-storage-format/
歡迎關(guān)注公眾號(hào): FullStackPlan 獲取更多干貨哦~