ORC的全稱是(Optimized Row Columnar),ORC文件格式是一種Hadoop生態圈中的列式存儲格式,它的產生早在2013年初,最初產生自Apache Hive,用於下降Hadoop數據存儲空間和加速Hive查詢速度。和Parquet相似,它並非一個單純的列式存儲格式,仍然是首先根據行組分割整個表,在每個行組內進行按列存儲。ORC文件是自描述的,它的元數據使用Protocol Buffers序列化,而且文件中的數據儘量的壓縮以下降存儲空間的消耗,目前也被Spark SQL、Presto等查詢引擎支持,可是Impala對於ORC目前沒有支持,仍然使用Parquet做爲主要的列式存儲格式。2015年ORC項目被Apache項目基金會提高爲Apache頂級項目。ORC具備如下一些優點:html
因爲OLAP查詢的特色,列式存儲能夠提高其查詢性能,可是它是如何作到的呢?這就要從列式存儲的原理提及,從圖1中能夠看到,相對於關係數據庫中一般使用的行式存儲,在使用列式存儲時每一列的全部元素都是順序存儲的。由此特色能夠給查詢帶來以下的優化:算法
關於Orc文件格式的官網介紹,見:數據庫
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORCapache
須要注意的是,ORC在讀寫時候須要消耗額外的CPU資源來壓縮和解壓縮,固然這部分的CPU消耗是很是少的。
數組
和Parquet不一樣,ORC原生是不支持嵌套數據格式的,而是經過對複雜數據類型特殊處理的方式實現嵌套格式的支持,例如對於以下的hive表:緩存
CREATE TABLE `orcStructTable`( `name` string, `course` struct<course:string,score:int>, `score` map<string,int>, `work_locations` array<string> )
在ORC的結構中包含了複雜類型列和原始類型,前者包括LIST、STRUCT、MAP和UNION類型,後者包括BOOLEAN、整數、浮點數、字符串類型等,其中STRUCT的孩子節點包括它的成員變量,可能有多個孩子節點,MAP有兩個孩子節點,分別爲key和value,LIST包含一個孩子節點,類型爲該LIST的成員類型,UNION通常不怎麼用獲得。每個Schema樹的根節點爲一個Struct類型,全部的column按照樹的中序遍歷順序編號。性能優化
ORC只須要存儲schema樹中葉子節點的值,而中間的非葉子節點只是作一層代理,它們只須要負責孩子節點值得讀取,只有真正的葉子節點纔會讀取數據,而後交由父節點封裝成對應的數據結構返回。數據結構
和Parquet相似,ORC文件也是以二進制方式存儲的,因此是不能夠直接讀取,ORC文件也是自解析的,它包含許多的元數據,這些元數據都是同構ProtoBuffer進行序列化的。ORC的文件結構以下圖,其中涉及到以下的概念:app
在ORC文件中保存了三個層級的統計信息,分別爲文件級別、stripe級別和row group級別的,他們均可以用來根據Search ARGuments(謂詞下推條件)判斷是否能夠跳過某些數據,在統計信息中都包含成員數和是否有null值,而且對於不一樣類型的數據設置一些特定的統計信息。ide
(1)file level
在ORC文件的末尾會記錄文件級別的統計信息,會記錄整個文件中columns的統計信息。這些信息主要用於查詢的優化,也能夠爲一些簡單的聚合查詢好比max, min, sum輸出結果。
(2)stripe level
ORC文件會保存每一個字段stripe級別的統計信息,ORC reader使用這些統計信息來肯定對於一個查詢語句來講,須要讀入哪些stripe中的記錄。好比說某個stripe的字段max(a)=10,min(a)=3,那麼當where條件爲a >10或者a <3時,那麼這個stripe中的全部記錄在查詢語句執行時不會被讀入。
(3)row level
爲了進一步的避免讀入沒必要要的數據,在邏輯上將一個column的index以一個給定的值(默認爲10000,可由參數配置)分割爲多個index組。以10000條記錄爲一個組,對數據進行統計。Hive查詢引擎會將where條件中的約束傳遞給ORC reader,這些reader根據組級別的統計信息,過濾掉沒必要要的數據。若是該值設置的過小,就會保存更多的統計信息,用戶須要根據本身數據的特色權衡一個合理的值
請參考:更高的壓縮比,更好的性能–使用ORC文件格式優化Hive
讀取ORC文件是從尾部開始的,第一次讀取16KB的大小,儘量的將Postscript和Footer數據都讀入內存。文件的最後一個字節保存着PostScript的長度,它的長度不會超過256字節,PostScript中保存着整個文件的元數據信息,它包括文件的壓縮格式、文件內部每個壓縮塊的最大長度(每次分配內存的大小)、Footer長度,以及一些版本信息。在Postscript和Footer之間存儲着整個文件的統計信息(上圖中未畫出),這部分的統計信息包括每個stripe中每一列的信息,主要統計成員數、最大值、最小值、是否有空值等。
接下來讀取文件的Footer信息,它包含了每個stripe的長度和偏移量,該文件的schema信息(將schema樹按照schema中的編號保存在數組中)、整個文件的統計信息以及每個row group的行數。
處理stripe時首先從Footer中獲取每個stripe的其實位置和長度、每個stripe的Footer數據(元數據,記錄了index和data的的長度),整個striper被分爲index和data兩部分,stripe內部是按照row group進行分塊的(每個row group中多少條記錄在文件的Footer中存儲),row group內部按列存儲。每個row group由多個stream保存數據和索引信息。每個stream的數據會根據該列的類型使用特定的壓縮算法保存。在ORC中存在以下幾種stream類型:
在初始化階段獲取所有的元數據以後,能夠經過includes數組指定須要讀取的列編號,它是一個boolean數組,若是不指定則讀取所有的列,還能夠經過傳遞SearchArgument參數指定過濾條件,根據元數據首先讀取每個stripe中的index信息,而後根據index中統計信息以及SearchArgument參數肯定須要讀取的row group編號,再根據includes數據決定須要從這些row group中讀取的列,經過這兩層的過濾須要讀取的數據只是整個stripe多個小段的區間,而後ORC會盡量合併多個離散的區間儘量的減小I/O次數。而後再根據index中保存的下一個row group的位置信息調至該stripe中第一個須要讀取的row group中。
ORC文件格式只支持讀取指定字段,還不支持只讀取特殊字段類型中的指定部分。
使用ORC文件格式時,用戶可使用HDFS的每個block存儲ORC文件的一個stripe。對於一個ORC文件來講,stripe的大小通常須要設置得比HDFS的block小,若是不這樣的話,一個stripe就會分別在HDFS的多個block上,當讀取這種數據時就會發生遠程讀數據的行爲。若是設置stripe的只保存在一個block上的話,若是當前block上的剩餘空間不足以存儲下一個strpie,ORC的writer接下來會將數據打散保存在block剩餘的空間上,直到這個block存滿爲止。這樣,下一個stripe又會從下一個block開始存儲。
因爲ORC中使用了更加精確的索引信息,使得在讀取數據時能夠指定從任意一行開始讀取,更細粒度的統計信息使得讀取ORC文件跳過整個row group,ORC默認會對任何一塊數據和索引信息使用ZLIB壓縮,所以ORC文件佔用的存儲空間也更小,這點在後面的測試對比中也有所印證。
關於row group index和bloom filter index的性能優化,請參考Hive性能優化之ORC索引–Row Group Index vs Bloom Filter Index
ORC文件使用兩級壓縮機制,首先將一個數據流使用流式編碼器進行編碼,而後使用一個可選的壓縮器對數據流進行進一步壓縮。
一個column可能保存在一個或多個數據流中,能夠將數據流劃分爲如下四種類型:
• Byte Stream
字節流保存一系列的字節數據,不對數據進行編碼。
• Run Length Byte Stream
字節長度字節流保存一系列的字節數據,對於相同的字節,保存這個重複值以及該值在字節流中出現的位置。
• Integer Stream
整形數據流保存一系列整形數據。能夠對數據量進行字節長度編碼以及delta編碼。具體使用哪一種編碼方式須要根據整形流中的子序列模式來肯定。
• Bit Field Stream
比特流主要用來保存boolean值組成的序列,一個字節表明一個boolean值,在比特流的底層是用Run Length Byte Stream來實現的。
接下來會以Integer和String類型的字段舉例來講明。
(1)Integer
對於一個整形字段,會同時使用一個比特流和整形流。比特流用於標識某個值是否爲null,整形流用於保存該整形字段非空記錄的整數值。
(2)String
對於一個String類型字段,ORC writer在開始時會檢查該字段值中不一樣的內容數佔非空記錄總數的百分比不超過0.8的話,就使用字典編碼,字段值會保存在一個比特流,一個字節流及兩個整形流中。比特流也是用於標識null值的,字節流用於存儲字典值,一個整形流用於存儲字典中每一個詞條的長度,另外一個整形流用於記錄字段值。
若是不能用字典編碼,ORC writer會知道這個字段的重複值太少,用字典編碼效率不高,ORC writer會使用一個字節流保存String字段的值,而後用一個整形流來保存每一個字段的字節長度。
在ORC文件中,在各類數據流的底層,用戶能夠自選ZLIB, Snappy和LZO壓縮方式對數據流進行壓縮。編碼器通常會將一個數據流壓縮成一個個小的壓縮單元,在目前的實現中,壓縮單元的默認大小是256KB。
在建Hive表的時候咱們就應該指定文件的存儲格式。因此你能夠在Hive QL語句裏面指定用ORCFile這種文件格式,以下:
CREATE TABLE ... STORED AS ORC ALTER TABLE ... [PARTITION partition_spec] SET FILEFORMAT ORC SET hive.default.fileformat=Orc
全部關於ORCFile的參數都是在Hive QL語句的TBLPROPERTIES字段裏面出現,他們是:
Key |
Default |
Notes |
orc.compress |
ZLIB |
high level compression (one of NONE, ZLIB, SNAPPY) |
orc.compress.size |
262,144 |
number of bytes in each compression chunk |
orc.stripe.size |
268435456 |
number of bytes in each stripe |
orc.row.index.stride |
10,000 |
number of rows between index entries (must be >= 1000) |
orc.create.index |
true |
whether to create row indexes |
到https://orc.apache.org官網下載orc源碼包,而後編譯獲取orc-core-1.3.0.jar、orc-mapreduce-1.3.0.jar、orc-tools-1.3.0.jar,將其加入項目中
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hive.ql.exec.vector.LongColumnVector; import org.apache.hadoop.hive.ql.exec.vector.VectorizedRowBatch; import org.apache.orc.CompressionKind; import org.apache.orc.OrcFile; import org.apache.orc.TypeDescription; import org.apache.orc.Writer; public class TestORCWriter { public static void main(String[] args) throws Exception { Path testFilePath = new Path("/tmp/test.orc"); Configuration conf = new Configuration(); TypeDescription schema = TypeDescription.fromString("struct<field1:int,field2:int,field3:int>"); Writer writer = OrcFile.createWriter(testFilePath, OrcFile.writerOptions(conf).setSchema(schema).compress(CompressionKind.SNAPPY)); VectorizedRowBatch batch = schema.createRowBatch(); LongColumnVector first = (LongColumnVector) batch.cols[0]; LongColumnVector second = (LongColumnVector) batch.cols[1]; LongColumnVector third = (LongColumnVector) batch.cols[2]; final int BATCH_SIZE = batch.getMaxSize(); // add 1500 rows to file
for (int r = 0; r < 15000000; ++r) { int row = batch.size++; first.vector[row] = r; second.vector[row] = r * 3; third.vector[row] = r * 6; if (row == BATCH_SIZE - 1) { writer.addRowBatch(batch); batch.reset(); } } if (batch.size != 0) { writer.addRowBatch(batch); batch.reset(); } writer.close(); } }
大多狀況下,仍是建議在Hive中將文本文件轉成ORC格式,這種用JAVA在本地生成ORC文件,屬於特殊需求場景。
http://lxw1234.com/archives/2016/04/630.htm
https://www.iteblog.com/archives/1014.html
http://blog.csdn.net/dabokele/article/details/51542327
http://blog.csdn.net/dabokele/article/details/51813322