refer:https://www.cnblogs.com/ITtangtang/p/7677912.html
#ORC File文件結構
#ORC的全稱是(Optimized Row Columnar)劈狐,ORC文件格式是一種Hadoop生態(tài)圈中的列式存儲格式,它的產(chǎn)生早在2013年初呐馆,最初產(chǎn)生自Apache Hive肥缔,用于降低Hadoop數(shù)據(jù)存儲空間和加速Hive查詢速度。和Parquet類似汹来,它并不是一個單純的列式存儲格式续膳,仍然是首先根據(jù)行組分割整個表改艇,在每一個行組內(nèi)進行按列存儲。ORC文件是自描述的坟岔,它的元數(shù)據(jù)使用Protocol Buffers序列化谒兄,并且文件中的數(shù)據(jù)盡可能的壓縮以降低存儲空間的消耗,目前也被Spark SQL社付、Presto等查詢引擎支持承疲,但是Impala對于ORC目前沒有支持,仍然使用Parquet作為主要的列式存儲格式鸥咖。2015年ORC項目被Apache項目基金會提升為Apache頂級項目燕鸽。ORC具有以下一些優(yōu)勢:
#ORC是列式存儲,有多種文件壓縮方式啼辣,并且有著很高的壓縮比啊研。
#文件是可切分(Split)的。因此鸥拧,在Hive中使用ORC作為表的文件存儲格式党远,不僅節(jié)省HDFS存儲資源,查詢?nèi)蝿盏妮斎霐?shù)據(jù)量減少富弦,使用的MapTask也就減少了沟娱。
#提供了多種索引,row group index舆声、bloom filter index花沉。
#ORC可以支持復雜的數(shù)據(jù)結構(比如Map等)
#列式存儲
由于OLAP查詢的特點柳爽,列式存儲可以提升其查詢性能媳握,但是它是如何做到的呢?這就要從列式存儲的原理說起磷脯,從圖1中可以看到蛾找,相對于關系數(shù)據(jù)庫中通常使用的行式存儲,在使用列式存儲時每一列的所有元素都是順序存儲的赵誓。由此特點可以給查詢帶來如下的優(yōu)化:
1.查詢的時候不需要掃描全部的數(shù)據(jù)打毛,而只需要讀取每次查詢涉及的列,這樣可以將I/O消耗降低N倍俩功,另外可以保存每一列的統(tǒng)計信息(min幻枉、max、sum等)诡蜓,實現(xiàn)部分的謂詞下推熬甫。
2.由于每一列的成員都是同構的,可以針對不同的數(shù)據(jù)類型使用更高效的數(shù)據(jù)壓縮算法蔓罚,進一步減小I/O椿肩。
3.由于每一列的成員的同構性瞻颂,可以使用更加適合CPU pipeline的編碼方式,減小CPU的緩存失效郑象。
#關于Orc文件格式的官網(wǎng)介紹贡这,見:
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC
#需要注意的是,ORC在讀寫時候需要消耗額外的CPU資源來壓縮和解壓縮厂榛,當然這部分的CPU消耗是非常少的盖矫。
#數(shù)據(jù)模型
#和Parquet不同,ORC原生是不支持嵌套數(shù)據(jù)格式的击奶,而是通過對復雜數(shù)據(jù)類型特殊處理的方式實現(xiàn)嵌套格式的支持炼彪,例如對于如下的hive表:
CREATE TABLE `orcStructTable`(
? `name` string,
? `course` struct,
? `score` map,
? `work_locations` array)
在ORC的結構中包含了復雜類型列和原始類型,前者包括LIST正歼、STRUCT辐马、MAP和UNION類型,后者包括BOOLEAN局义、整數(shù)喜爷、浮點數(shù)、字符串類型等萄唇,其中STRUCT的孩子節(jié)點包括它的成員變量檩帐,可能有多個孩子節(jié)點,MAP有兩個孩子節(jié)點另萤,分別為key和value湃密,LIST包含一個孩子節(jié)點,類型為該LIST的成員類型四敞,UNION一般不怎么用得到泛源。每一個Schema樹的根節(jié)點為一個Struct類型,所有的column按照樹的中序遍歷順序編號忿危。
ORC只需要存儲schema樹中葉子節(jié)點的值达箍,而中間的非葉子節(jié)點只是做一層代理,它們只需要負責孩子節(jié)點值得讀取铺厨,只有真正的葉子節(jié)點才會讀取數(shù)據(jù)缎玫,然后交由父節(jié)點封裝成對應的數(shù)據(jù)結構返回。
#文件結構
#和Parquet類似解滓,ORC文件也是以二進制方式存儲的秒赤,所以是不可以直接讀取媳维,ORC文件也是自解析的,它包含許多的元數(shù)據(jù),這些元數(shù)據(jù)都是同構ProtoBuffer進行序列化的鞍泉。ORC的文件結構如下圖,其中涉及到如下的概念:
ORC文件:保存在文件系統(tǒng)上的普通二進制文件,一個ORC文件中可以包含多個stripe,每一個stripe包含多條記錄在扰,這些記錄按照列進行獨立存儲,對應到Parquet中的row group的概念雷客。
文件級元數(shù)據(jù):包括文件的描述信息PostScript芒珠、文件meta信息(包括整個文件的統(tǒng)計信息)、所有stripe的信息和文件schema信息搅裙。
1.stripe:一組行形成一個stripe皱卓,每次讀取文件是以行組為單位的,一般為HDFS的塊大小部逮,保存了每一列的索引和數(shù)據(jù)娜汁。
2.stripe元數(shù)據(jù):保存stripe的位置、每一個列的在該stripe的統(tǒng)計信息以及所有的stream類型和位置兄朋。
3.row group:索引的最小單位掐禁,一個stripe中包含多個row group,默認為10000個值組成颅和。
4.stream:一個stream表示文件中一段有效的數(shù)據(jù)傅事,包括索引和數(shù)據(jù)兩類。索引stream保存每一個row group的位置和統(tǒng)計信息峡扩,數(shù)據(jù)stream包括多種類型的數(shù)據(jù)蹭越,具體需要哪幾種是由該列類型和編碼方式?jīng)Q定。
在ORC文件中保存了三個層級的統(tǒng)計信息教届,分別為文件級別响鹃、stripe級別和row group級別的,他們都可以用來根據(jù)Search ARGuments(謂詞下推條件)判斷是否可以跳過某些數(shù)據(jù)案训,在統(tǒng)計信息中都包含成員數(shù)和是否有null值买置,并且對于不同類型的數(shù)據(jù)設置一些特定的統(tǒng)計信息。
(1)file level
在ORC文件的末尾會記錄文件級別的統(tǒng)計信息萤衰,會記錄整個文件中columns的統(tǒng)計信息堕义。這些信息主要用于查詢的優(yōu)化,也可以為一些簡單的聚合查詢比如max, min, sum輸出結果脆栋。
(2)stripe level
ORC文件會保存每個字段stripe級別的統(tǒng)計信息,ORC reader使用這些統(tǒng)計信息來確定對于一個查詢語句來說洒擦,需要讀入哪些stripe中的記錄椿争。比如說某個stripe的字段max(a)=10,min(a)=3熟嫩,那么當where條件為a >10或者a <3時秦踪,那么這個stripe中的所有記錄在查詢語句執(zhí)行時不會被讀入。
(3)row level
為了進一步的避免讀入不必要的數(shù)據(jù),在邏輯上將一個column的index以一個給定的值(默認為10000椅邓,可由參數(shù)配置)分割為多個index組柠逞。以10000條記錄為一個組,對數(shù)據(jù)進行統(tǒng)計景馁。Hive查詢引擎會將where條件中的約束傳遞給ORC reader板壮,這些reader根據(jù)組級別的統(tǒng)計信息,過濾掉不必要的數(shù)據(jù)合住。如果該值設置的太小绰精,就會保存更多的統(tǒng)計信息,用戶需要根據(jù)自己數(shù)據(jù)的特點權衡一個合理的值
#ORC元數(shù)據(jù)
請參考:更高的壓縮比透葛,更好的性能–使用ORC文件格式優(yōu)化Hive
#數(shù)據(jù)訪問
讀取ORC文件是從尾部開始的笨使,第一次讀取16KB的大小,盡可能的將Postscript和Footer數(shù)據(jù)都讀入內(nèi)存僚害。文件的最后一個字節(jié)保存著PostScript的長度硫椰,它的長度不會超過256字節(jié),PostScript中保存著整個文件的元數(shù)據(jù)信息萨蚕,它包括文件的壓縮格式最爬、文件內(nèi)部每一個壓縮塊的最大長度(每次分配內(nèi)存的大小)、Footer長度门岔,以及一些版本信息爱致。在Postscript和Footer之間存儲著整個文件的統(tǒng)計信息(上圖中未畫出),這部分的統(tǒng)計信息包括每一個stripe中每一列的信息寒随,主要統(tǒng)計成員數(shù)糠悯、最大值、最小值妻往、是否有空值等互艾。
接下來讀取文件的Footer信息,它包含了每一個stripe的長度和偏移量讯泣,該文件的schema信息(將schema樹按照schema中的編號保存在數(shù)組中)纫普、整個文件的統(tǒng)計信息以及每一個row group的行數(shù)。
處理stripe時首先從Footer中獲取每一個stripe的其實位置和長度好渠、每一個stripe的Footer數(shù)據(jù)(元數(shù)據(jù)昨稼,記錄了index和data的的長度),整個striper被分為index和data兩部分拳锚,stripe內(nèi)部是按照row group進行分塊的(每一個row group中多少條記錄在文件的Footer中存儲)假栓,row group內(nèi)部按列存儲。每一個row group由多個stream保存數(shù)據(jù)和索引信息霍掺。每一個stream的數(shù)據(jù)會根據(jù)該列的類型使用特定的壓縮算法保存匾荆。在ORC中存在如下幾種stream類型:
PRESENT:每一個成員值在這個stream中保持一位(bit)用于標示該值是否為NULL拌蜘,通過它可以只記錄部位NULL的值
DATA:該列的中屬于當前stripe的成員值。
LENGTH:每一個成員的長度牙丽,這個是針對string類型的列才有的简卧。
DICTIONARY_DATA:對string類型數(shù)據(jù)編碼之后字典的內(nèi)容。
SECONDARY:存儲Decimal烤芦、timestamp類型的小數(shù)或者納秒數(shù)等举娩。
ROW_INDEX:保存stripe中每一個row group的統(tǒng)計信息和每一個row group起始位置信息。
在初始化階段獲取全部的元數(shù)據(jù)之后拍棕,可以通過includes數(shù)組指定需要讀取的列編號晓铆,它是一個boolean數(shù)組,如果不指定則讀取全部的列绰播,還可以通過傳遞SearchArgument參數(shù)指定過濾條件骄噪,根據(jù)元數(shù)據(jù)首先讀取每一個stripe中的index信息,然后根據(jù)index中統(tǒng)計信息以及SearchArgument參數(shù)確定需要讀取的row group編號蠢箩,再根據(jù)includes數(shù)據(jù)決定需要從這些row group中讀取的列链蕊,通過這兩層的過濾需要讀取的數(shù)據(jù)只是整個stripe多個小段的區(qū)間,然后ORC會盡可能合并多個離散的區(qū)間盡可能的減少I/O次數(shù)谬泌。然后再根據(jù)index中保存的下一個row group的位置信息調至該stripe中第一個需要讀取的row group中滔韵。
ORC文件格式只支持讀取指定字段,還不支持只讀取特殊字段類型中的指定部分掌实。?
使用ORC文件格式時陪蜻,用戶可以使用HDFS的每一個block存儲ORC文件的一個stripe。對于一個ORC文件來說贱鼻,stripe的大小一般需要設置得比HDFS的block小宴卖,如果不這樣的話,一個stripe就會分別在HDFS的多個block上邻悬,當讀取這種數(shù)據(jù)時就會發(fā)生遠程讀數(shù)據(jù)的行為症昏。如果設置stripe的只保存在一個block上的話,如果當前block上的剩余空間不足以存儲下一個strpie父丰,ORC的writer接下來會將數(shù)據(jù)打散保存在block剩余的空間上肝谭,直到這個block存滿為止。這樣蛾扇,下一個stripe又會從下一個block開始存儲攘烛。
由于ORC中使用了更加精確的索引信息,使得在讀取數(shù)據(jù)時可以指定從任意一行開始讀取屁桑,更細粒度的統(tǒng)計信息使得讀取ORC文件跳過整個row group医寿,ORC默認會對任何一塊數(shù)據(jù)和索引信息使用ZLIB壓縮,因此ORC文件占用的存儲空間也更小蘑斧,這點在后面的測試對比中也有所印證。
關于row group index和bloom filter index的性能優(yōu)化,請參考Hive性能優(yōu)化之ORC索引–Row Group Index vs Bloom Filter Index
#文件壓縮
ORC文件使用兩級壓縮機制竖瘾,首先將一個數(shù)據(jù)流使用流式編碼器進行編碼沟突,然后使用一個可選的壓縮器對數(shù)據(jù)流進行進一步壓縮。?
一個column可能保存在一個或多個數(shù)據(jù)流中捕传,可以將數(shù)據(jù)流劃分為以下四種類型:?
? Byte Stream
字節(jié)流保存一系列的字節(jié)數(shù)據(jù)惠拭,不對數(shù)據(jù)進行編碼。?
? Run Length Byte Stream
字節(jié)長度字節(jié)流保存一系列的字節(jié)數(shù)據(jù)庸论,對于相同的字節(jié)职辅,保存這個重復值以及該值在字節(jié)流中出現(xiàn)的位置。?
? Integer Stream
整形數(shù)據(jù)流保存一系列整形數(shù)據(jù)聂示∮蛐可以對數(shù)據(jù)量進行字節(jié)長度編碼以及delta編碼。具體使用哪種編碼方式需要根據(jù)整形流中的子序列模式來確定鱼喉。?
? Bit Field Stream
比特流主要用來保存boolean值組成的序列秀鞭,一個字節(jié)代表一個boolean值,在比特流的底層是用Run Length Byte Stream來實現(xiàn)的扛禽。?
#接下來會以Integer和String類型的字段舉例來說明锋边。?
(1)Integer
對于一個整形字段,會同時使用一個比特流和整形流编曼。比特流用于標識某個值是否為null豆巨,整形流用于保存該整形字段非空記錄的整數(shù)值。?
(2)String
對于一個String類型字段掐场,ORC writer在開始時會檢查該字段值中不同的內(nèi)容數(shù)占非空記錄總數(shù)的百分比不超過0.8的話往扔,就使用字典編碼,字段值會保存在一個比特流刻肄,一個字節(jié)流及兩個整形流中瓤球。比特流也是用于標識null值的,字節(jié)流用于存儲字典值敏弃,一個整形流用于存儲字典中每個詞條的長度卦羡,另一個整形流用于記錄字段值。?
如果不能用字典編碼麦到,ORC writer會知道這個字段的重復值太少绿饵,用字典編碼效率不高,ORC writer會使用一個字節(jié)流保存String字段的值瓶颠,然后用一個整形流來保存每個字段的字節(jié)長度拟赊。?
在ORC文件中,在各種數(shù)據(jù)流的底層粹淋,用戶可以自選ZLIB, Snappy和LZO壓縮方式對數(shù)據(jù)流進行壓縮吸祟。編碼器一般會將一個數(shù)據(jù)流壓縮成一個個小的壓縮單元瑟慈,在目前的實現(xiàn)中,壓縮單元的默認大小是256KB屋匕。
#參數(shù)可參看:
#