Hive數倉建表該選用ORC還是Parquet,壓縮選LZO還是Snappy?


在數倉中,建議大家除了接口表(從其他數據庫導入或者是最后要導出到其他數據庫的表),其余表的存儲格式與壓縮格式保持一致。

我們先來說一下目前Hive表主流的存儲格式與壓縮方式。

文件存儲格式

從Hive官網得知,Apache Hive支持Apache Hadoop中使用的幾種熟悉的文件格式,如 TextFile(文本格式)RCFile(行列式文件)SequenceFile(二進制序列化文件)AVROORC(優化的行列式文件)Parquet 格式,而這其中我們目前使用最多的是TextFileSequenceFileORCParquet

下面來詳細了解下這2種行列式存儲。

1、ORC

1.1 ORC的存儲結構

我們先從官網上拿到ORC的存儲模型圖

看起來略微有點復雜,那我們稍微簡化一下,我畫了一個簡單的圖來說明一下

 

左邊的圖就表示了傳統的行式數據庫存儲方式,按行存儲,如果沒有存儲索引的話,如果需要查詢一個字段,就需要把整行的數據都查出來然后做篩選,這么做式比較消耗IO資源的,於是在Hive種最開始也是用了索引的方式來解決這個問題。

但是由於索引的高成本,在「目前的Hive3.X 中,已經廢除了索引」,當然也早就引入了列式存儲。

列式存儲的存儲方式,其實和名字一樣,它是按照一列一列存儲的,如上圖中的右圖,這樣的話如果查詢一個字段的數據,就等於是索引查詢,效率高。但是如果需要查全表,它因為需要分別取所有的列最后匯總,反而更占用資源。於是ORC行列式存儲出現了。

  1. 在需要全表掃描時,可以按照行組讀取
  2. 如果需要取列數據,在行組的基礎上,讀取指定的列,而不需要所有行組內所有行的數據和一行內所有字段的數據。

了解了ORC存儲的基本邏輯后,我們再來看看它的存儲模型圖。

 

同時我也把詳細的文字也附在下面,大家可以對照着看看:

  • 條帶( stripe):ORC文件存儲數據的地方,每個stripe一般為HDFS的塊大小。(包含以下3部分)
index data:保存了所在條帶的一些統計信息,以及數據在 stripe中的位置索引信息。 rows data:數據存儲的地方,由多個行組構成,每10000行構成一個行組,數據以流( stream)的形式進行存儲。 stripe footer:保存數據所在的文件目錄 
  • 文件腳注( file footer):包含了文件中sipe的列表,每個 stripe的行數,以及每個列的數據類型。它還包含每個列的最小值、最大值、行計數、求和等聚合信息。
  • postscript:含有壓縮參數和壓縮大小相關的信息

所以其實發現,ORC提供了3級索引,文件級、條帶級、行組級,所以在查詢的時候,利用這些索引可以規避大部分不滿足查詢條件的文件和數據塊。

但注意,ORC中所有數據的描述信息都是和存儲的數據放在一起的,並沒有借助外部的數據庫。

「特別注意:ORC格式的表還支持事務ACID,但是支持事務的表必須為分桶表,所以適用於更新大批量的數據,不建議用事務頻繁的更新小批量的數據」

#開啟並發支持,支持插入、刪除和更新的事務 set hive. support concurrency=truei #支持ACID事務的表必須為分桶表 set hive. enforce bucketing=truei #開啟事物需要開啟動態分區非嚴格模式 set hive.exec,dynamicpartition.mode-nonstrict #設置事務所管理類型為 org. apache.hive.q1. lockage. DbTxnManager #原有的org. apache. hadoop.hive.q1.1 eckmar. DummyTxnManager不支持事務 set hive. txn. manager=org. apache. hadoop. hive. q1. lockmgr DbTxnManageri #開啟在相同的一個 meatore實例運行初始化和清理的線程 set hive. compactor initiator on=true: #設置每個 metastore實例運行的線程數 hadoop set hive. compactor. worker threads=l #(2)創建表 create table student_txn (id int, name string ) #必須支持分桶 clustered by (id) into 2 buckets #在表屬性中添加支持事務 stored as orc TBLPROPERTIES('transactional'='true‘); #(3)插入數據 #插入id為1001,名字為student 1001 insert into table student_txn values('1001','student 1001'); #(4)更新數據 #更新數據 update student_txn set name= 'student 1zh' where id='1001'; # (5)查看表的數據,最終會發現id為1001被改為 sutdent_1zh 

1.2關於ORC的Hive配置

表配置屬性(建表時配置,例如tblproperties ('orc.compress'='snappy');

  • orc.compress:表示ORC文件的壓縮類型,「可選的類型有NONE、ZLB和SNAPPY,默認值是ZLIB(Snappy不支持切片)」---這個配置是最關鍵的。
  • orc. compress.Slze:表示壓縮塊( chunk)的大小,默認值是262144(256KB)。
  • orc. stripe.size:寫 stripe,可以使用的內存緩沖池大小,默認值是67108864(64MB)
  • orc. row. index. stride:行組級別索引的數據量大小,默認是10000,必須要設置成大於等於10000的數
  • orc. create index:是否創建行組級別索引,默認是true
  • orc. bloom filter. columns:需要創建布隆過濾的組。
  • orc. bloom filter fpp:使用布隆過濾器的假正( False Positive)概率,默認值是0. 擴展:在Hive中使用 bloom過濾器,可以用較少的文件空間快速判定數據是否存表中,但是也存在將不屬於這個表的數據判定為屬於這個這表的情況,這個稱之為假正概率,開發者可以調整該概率,但概率越低,布隆過濾器所需要。

2、Parquet

上面說過ORC后,我們對行列式存儲也有了基本的了解,而Parquet是另一種高性能的行列式存儲結構。

2.1 Parquet的存儲結構

既然ORC都那么高效了,那為什么還要再來一個Parquet,那是因為「Parquet是為了使Hadoop生態系統中的任何項目都可以使用壓縮的,高效的列式數據表示形式」

❝ Parquet 是語言無關的,而且不與任何一種數據處理框架綁定在一起,適配多種語言和組件,能夠與 Parquet 配合的組件有:
查詢引擎: Hive, Impala, Pig, Presto, Drill, Tajo, HAWQ, IBM Big SQL
計算框架: MapReduce, Spark, Cascading, Crunch, Scalding, Kite
數據模型: Avro, Thrift, Protocol Buffers, POJOs

再來看看Parquet的存儲結構吧,先看官網給的

嗯,略微有點頭大,我畫個簡易版

 

Parquet文件是以二進制方式存儲的,所以不可以直接讀取,和ORC一樣,文件的元數據和數據一起存儲,所以Parquet格式文件是自解析的。

  1. 行組(Row Group):每一個行組包含一定的行數,在一個HDFS文件中至少存儲一個行組,類似於orc的stripe的概念。
  2. 列塊(Column Chunk):在一個行組中每一列保存在一個列塊中,行組中的所有列連續的存儲在這個行組文件中。一個列塊中的值都是相同類型的,不同的列塊可能使用不同的算法進行壓縮。
  3. 頁(Page):每一個列塊划分為多個頁,一個頁是最小的編碼的單位,在同一個列塊的不同頁可能使用不同的編碼方式。

2.2Parquet的表配置屬性

  • parquet. block size:默認值為134217728byte,即128MB,表示 Row Group在內存中的塊大小。該值設置得大,可以提升 Parquet文件的讀取效率,但是相應在寫的時候需要耗費更多的內存
  • parquet. page:size:默認值為1048576byt,即1MB,表示每個頁(page)的大小。這個特指壓縮后的頁大小,在讀取時會先將頁的數據進行解壓。頁是 Parquet操作數據的最小單位,每次讀取時必須讀完一整頁的數據才能訪問數據。這個值如果設置得過小,會導致壓縮時出現性能問題
  • parquet. compression:默認值為 UNCOMPRESSED,表示頁的壓縮方式。「可以使用的壓縮方式有 UNCOMPRESSED、 SNAPPY、GZP和LZO」。
  • Parquet enable. dictionary:默認為tue,表示是否啟用字典編碼。
  • parquet. dictionary page.size:默認值為1048576byte,即1MB。在使用字典編碼時,會在 Parquet的每行每列中創建一個字典頁。使用字典編碼,如果存儲的數據頁中重復的數據較多,能夠起到一個很好的壓縮效果,也能減少每個頁在內存的占用。

3、ORC和Parquet對比

 

同時,從《Hive性能調優實戰》作者的案例中,2張分別采用ORC和Parquet存儲格式的表,導入同樣的數據,進行sql查詢,「發現使用ORC讀取的行遠小於Parquet」,所以使用ORC作為存儲,可以借助元數據過濾掉更多不需要的數據,查詢時需要的集群資源比Parquet更少。(查看更詳細的性能分析,請移步

「所以ORC在存儲方面看起來還是更勝一籌」

壓縮方式

ble data-draft-node="block" data-draft-type="table" data-size="normal" data-row-style="normal">

存儲和壓縮結合該如何選擇?

根據ORC和parquet的要求,一般就有了

1、ORC格式存儲,Snappy壓縮

create table stu_orc(id int,name string) stored as orc tblproperties ('orc.compress'='snappy'); 

2、Parquet格式存儲,Lzo壓縮

create table stu_par(id int,name string) stored as parquet tblproperties ('parquet.compression'='lzo'); 

3、Parquet格式存儲,Snappy壓縮

create table stu_par(id int,name string) stored as parquet tblproperties ('parquet.compression'='snappy'); 

因為Hive 的SQL會轉化為MR任務,如果該文件是用ORC存儲,Snappy壓縮的,因為Snappy不支持文件分割操作,所以壓縮文件「只會被一個任務所讀取」,如果該壓縮文件很大,那么處理該文件的Map需要花費的時間會遠多於讀取普通文件的Map時間,這就是常說的「Map讀取文件的數據傾斜」。

那么為了避免這種情況的發生,就需要在數據壓縮的時候采用bzip2和Zip等支持文件分割的壓縮算法。但恰恰ORC不支持剛說到的這些壓縮方式,所以這也就成為了大家在可能遇到大文件的情況下不選擇ORC的原因,避免數據傾斜。

在Hve on Spark的方式中,也是一樣的,Spark作為分布式架構,通常會嘗試從多個不同機器上一起讀入數據。要實現這種情況,每個工作節點都必須能夠找到一條新記錄的開端,也就需要該文件可以進行分割,但是有些不可以分割的壓縮格式的文件,必須要單個節點來讀入所有數據,這就很容易產生性能瓶頸。(下一篇文章詳細寫Spark讀取文件的源碼分析)

「所以在實際生產中,使用Parquet存儲,lzo壓縮的方式更為常見,這種情況下可以避免由於讀取不可分割大文件引發的數據傾斜。 但是,如果數據量並不大(預測不會有超大文件,若干G以上)的情況下,使用ORC存儲,snappy壓縮的效率還是非常高的。」


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM