Hive數倉建表該選用ORC還是Parquet,壓縮選LZO還是Snappy?
在數倉中,建議大家除了介面表(從其他資料庫匯入或者是最後要匯出到其他資料庫的表),其餘表的儲存格式與壓縮格式保持一致。
我們先來說一下目前Hive表主流的儲存格式與壓縮方式。
檔案儲存格式
從Hive官網得知,Apache Hive支援Apache Hadoop中使用的幾種熟悉的檔案格式,如TextFile(文字格式)
,RCFile(行列式檔案)
,SequenceFile(二進位制序列化檔案)
,AVRO
,ORC(優化的行列式檔案)
和Parquet
格式,而這其中我們目前使用最多的是TextFile
,SequenceFile
,ORC
和Parquet
。
下面來詳細瞭解下這2種行列式儲存。
1、ORC
1.1 ORC的儲存結構
我們先從官網上拿到ORC的儲存模型圖
看起來略微有點複雜,那我們稍微簡化一下,我畫了一個簡單的圖來說明一下
左邊的圖就表示了傳統的行式資料庫儲存方式,按行儲存,如果沒有儲存索引的話,如果需要查詢一個欄位,就需要把整行的資料都查出來然後做篩選,這麼做式比較消耗IO資源的,於是在Hive種最開始也是用了索引的方式來解決這個問題。
但是由於索引的高成本,在「目前的Hive3.X 中,已經廢除了索引」,當然也早就引入了列式儲存。
列式儲存的儲存方式,其實和名字一樣,它是按照一列一列儲存的,如上圖中的右圖,這樣的話如果查詢一個欄位的資料,就等於是索引查詢,效率高。但是如果需要查全表,它因為需要分別取所有的列最後彙總,反而更佔用資源。於是ORC行列式儲存出現了。
- 在需要全表掃描時,可以按照行組讀取
- 如果需要取列資料,在行組的基礎上,讀取指定的列,而不需要所有行組內所有行的資料和一行內所有欄位的資料。
瞭解了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格式檔案是自解析的。
- 行組(Row Group):每一個行組包含一定的行數,在一個HDFS檔案中至少儲存一個行組,類似於orc的stripe的概念。
- 列塊(Column Chunk):在一個行組中每一列儲存在一個列塊中,行組中的所有列連續的儲存在這個行組檔案中。一個列塊中的值都是相同型別的,不同的列塊可能使用不同的演算法進行壓縮。
- 頁(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更少。(檢視更詳細的效能分析,請移步https://blog.csdn.net/yu616568/article/details/51188479)
「所以ORC在儲存方面看起來還是更勝一籌」
壓縮方式
bledata-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壓縮的效率還是非常高的。」