Hive總結篇及Hive的優化
概述
Hive學習也有一段時間了,今天來對Hive進行一個總結,談談自己的理解,作者還是個小白,有不對的地方請大家指出相互學習,共同進步。今天來談一談什麼是Hive,產生背景,優勢等一系列問題。
什麼是Hive
老規矩:官網地址 Hive wiki. 先來談談自己的理解: 有些人可能會說Hive不就是寫SQL的嗎,那我們其實可以從另一個角度來理解:Hive就是那麼強大啊,只要寫SQL就能解決問題,其實這些人說的也沒錯Hive確實就是寫SQL的,對於傳統的 DBA人員或者會寫SQL就很容易上手了,但是您知道他的底層細節嗎,怎麼優化呢?和傳統的關係型資料庫又有什麼區別呢?等等一系列問題。。。
Hive是一個構建在Hadoop之上的資料倉庫軟體,它可以使已經儲存的資料結構化,它提供類似sql的查詢語句HiveQL對資料進行分析處理。 Hive將HiveQL語句轉換成一系列成MapReduce作業並執行(SQL轉化為MapReduce的過程你知道嗎?)。使用者可以很方便的使用命令列和JDBC程式的方式來連線到hive。 目前,Hive除了支援MapReduce計算引擎,還支援Spark和Tez這兩中分散式計算引擎。常用於離線批處理。 (Hive On Spark 還是試驗版本)
Hive的產生背景
大資料的時代,海量的資料對於傳統的關係型資料庫來說維護起來成本非常高,那該如何是好,Hadoop分散式的框架,可以使用廉價的機器部署分散式系統把資料儲存再HDFS之上,通過MR進行計算,分析,這樣是可以的,但是,MR大家應該知道,MapReduce程式設計帶來的不便性,程式設計十分繁瑣,在大多情況下,每個MapReduce程式需要包含Mapper、Reduceer和一個Driver,之後需要打成jar包扔到叢集上運 行。如果mr寫完之後,且該專案已經上線,一旦業務邏輯發生了改變,可能就會帶來大規模的改動程式碼,然後重新打包,釋出,非常麻煩(這種方式,也是最古老的方式) 當大量資料都存放在HDFS上,如何快速的對HDFS上的檔案進行統計分析操作? 一般來說,想要做會有兩種方式: 1. 學Java、學MapReduce(十分麻煩) 2. 做DBA的:寫SQL(希望能通過寫SQL這樣的方式來實現,這種方式較好) 然而,HDFS中最關鍵的一點就是,資料儲存HDFS上是沒有schema的概念的(schema:相當於表裡面有列、欄位、欄位名稱、欄位與欄位之間的分隔符等,這些就是schema資訊)然而HDFS上的僅僅只是一個純的文字檔案而已,那麼,沒有schema,就沒辦法使用sql進行查詢了啊。。。因此,在這種背景下,就有問題產生:如何為HDFS上的檔案新增Schema資訊?如果加上去,是否就可以通過SQL的方式進行處理了呢?於是強大的Hive出現了。
Hive深入剖析
再來看看官網給我們的介紹:
官方第一句話就說明了Apache Hive 是構建在Apache Hadoop之上的資料倉庫。有助於對大型的資料集進行讀、寫和管理。
那我們先對這句話進行剖析:
首先Hive是構建在Hadoop之上的,其實就是Hive中的資料其實是儲存再HDFS上的(加上LOCAL關鍵字則是在本地),預設在/user/hive/warehouse/table
,有助於對大型資料集進行讀、寫和管理,那也就是意味著傳統的關係型資料庫已經無法滿足現在的資料量了,需要一個更大的倉庫來幫助我們儲存,這裡也引出一個問題:Hive和關係型資料庫的區別,後面我們再來聊。
Hive的特徵:
1.可通過SQL輕鬆訪問資料的工具,從而實現資料倉庫任務,如提取/轉換/載入(ETL),報告和資料分析。
2.它可以使已經儲存的資料結構化
3.可以直接訪問儲存在Apache HDFS™或其他資料儲存系統(如Apache HBase™)中的檔案
4.Hive除了支援MapReduce計算引擎,還支援Spark和Tez這兩中分散式計算引擎(這裡會引申出一個問題,哪些查詢跑mr哪些不跑?)
5.它提供類似sql的查詢語句HiveQL對資料進行分析處理。
6. 資料的儲存格式有多種,比如資料來源是二進位制格式, 普通文字格式等等
而hive強大之處不要求資料轉換成特定的格式,而是利用hadoop本身InputFormat API來從不同的資料來源讀取資料,同樣地使用OutputFormat API將資料寫成不同的格式。所以對於不同的資料來源,或者寫出不同的格式就需要不同的對應的InputFormat和Outputformat類的實現。 以stored as textfile為例,其在底層java API中表現是輸入InputFormat格式:TextInputFormat以及輸出OutputFormat格式:HiveIgnoreKeyTextOutputFormat.這裡InputFormat中定義瞭如何對資料來源文字進行讀取劃分,以及如何將切片分割成記錄存入表中。而Outputformat定義瞭如何將這些切片寫回到檔案裡或者直接在控制檯輸出。
不僅如此Hive的SQL還可以通過使用者定義的函式(UDF),使用者定義的聚合(UDAF)和使用者定義的表函式(UDTF)進行擴充套件。
(幾個函式之間的區別)
Hive中不僅可以使用逗號和製表符分隔值(CSV / TSV)文字檔案,還可以使用Sequence File、RC、ORC、Parquet
(知道這幾種儲存格式的區別),
當然Hive還可以通過使用者來自定義自己的儲存格式,基本上前面說的到的幾種格式完全夠了。
Hive旨在最大限度地提高可伸縮性(通過向Hadoop叢集動態新增更多機器擴充套件),效能,可擴充套件性,
容錯性以及與其輸入格式的鬆散耦合。
安裝部署
安裝部署這裡我們就不講解了,不會的同學,參考作者以前的部落格
Hive基本語法
改篇部落格主要講解Hive底層的東西和一些優化對於基本的東西可以參考作者以前的部落格。 DDL DML 基本SQL 內建函式和基本的UDF函式
UDF函式這裡要進行一個講解UDF、DUAF、UDTF分別是啥。 我們知道Hive的SQL還可以通過使用者定義的函式(UDF),使用者定義的聚合(UDAF)和使用者定義的表函式(UDTF)進行擴充套件。 當Hive提供的內建函式無法滿足你的業務處理需要時,此時就可以考慮使用使用者自定義函式(UDF:user-defined function)。 UDF(User-Defined-Function) 一進一出
UDAF(User- Defined Aggregation Funcation) 聚集函式,多進一出。
UDTF(User-Defined Table-Generating Functions) 一進多出,如lateral view explore()
Hive於關係型資料庫的區別
時效性、延時性比較高,可擴充套件性高;
Hive資料規模大,優勢在於處理大資料集,對於小資料集沒有優勢
事務沒什麼用(比較雞肋,沒什麼實際的意義,對於離線的來說) 一個小問題:那個版本開始提供了事務?
insert/update沒什麼實際用途,大資料場景下大多數是select
RDBMS也支援分散式,節點有限 成本高,處理的資料量小
Hadoop叢集規模更大 部署在廉價機器上,處理的資料量大
資料庫可以用在Online的應用中,Hive主要進行離線的大資料分析;
資料庫的查詢語句為SQL,Hive的查詢語句為HQL;
資料庫資料儲存在LocalFS,Hive的資料儲存在HDFS;
資料格式:Hive中有多種儲存格式:由於在載入資料的過程中,不需要從使用者資料格式到 Hive 定義的資料格式的轉換,
因此,Hive 在載入的過程中不會對資料本身進行任何修改,而只是將資料內容複製或者移動到相應的 HDFS 目錄中。
而在資料庫中,不同的資料庫有不同的儲存引擎,定義了自己的資料格式。所有資料都會按照一定的組織儲存,因此,
資料庫載入資料的過程會比較耗時。
Hive執行MapReduce,MySQL執行Executor;
Hive的優點
1.簡單易上手
2.擴充套件能力較好(指叢集 HDFS或是YARN)
3.統一的元資料管理 metastore包括的了資料庫,表,欄位分割槽等詳細資訊
4.由於統一的元資料管理所以和spark/impala等SQL引擎是通用的
通用是指,在擁有了統一的metastore之後,在Hive中建立一張表,在Spark/impala中是能用的,反之在Spark中建立一張表,
在Hive中也能用;只需要共用元資料,就可以切換SQL引擎
涉及到了Spark sql 和Hive On Spark(實驗版本)
5.使用SQL語法,提供快速開發的能力,支援自定義函式UDF。
6.避免了去寫mapreduce,減少開發人員學習成本。
7.資料離線處理,比如日誌分析,海量資料結構化分析
SQL轉化為MapReduce的過程
瞭解了MapReduce實現SQL基本操作之後,我們來看看Hive是如何將SQL轉化為MapReduce任務的,整個編譯過程分為六個階段: 1. Antlr定義SQL的語法規則,完成SQL詞法,語法解析,將SQL轉化為抽象語法樹AST Tree 2. 遍歷AST Tree,抽象出查詢的基本組成單元QueryBlock 3. 遍歷QueryBlock,翻譯為執行操作樹OperatorTree 4. 邏輯層優化器進行OperatorTree變換,合併不必要的ReduceSinkOperator,減少shuffle資料量 5. 遍歷OperatorTree,翻譯為MapReduce任務 6. 物理層優化器進行MapReduce任務的變換,生成最終的執行計劃
Hive內部表和外部表的區別
未被external修飾的是內部表(managed table),被external修飾的為外部表(external table); 區別: 內部表資料由Hive自身管理,外部表資料由HDFS管理; 內部表資料儲存的位置是hive.metastore.warehouse.dir(預設:/user/hive/warehouse),外部表資料的儲存位置由自己制定; 刪除內部表會直接刪除元資料(metadata)及儲存資料;刪除外部表僅僅會刪除元資料,HDFS上的檔案並不會被刪除;
行式儲存vs列式儲存
行式資料庫儲存在hdfs上式按行進行儲存的,一個block儲存一或多行資料。而列式資料庫在hdfs上則是按照列進行儲存,一個block可能有一列或多列資料。
如果要將資料進行壓縮: 對於行式資料庫,必然按行壓縮,當一行中有多個欄位,各個欄位對應的資料型別可能不一致,壓縮效能壓縮比就比較差。 對於列式資料庫,必然按列壓縮,每一列對應的是相同資料型別的資料,故列式資料庫的壓縮效能要強於行式資料庫。 如果要進行資料的查詢: 假設執行的查詢操作是:select id,name from table_emp; 對於行式資料庫,它要遍歷一整張表將每一行中的id,name欄位拼接再展現出來,這樣需要查詢的資料量就比較大,效率低。 對於列式資料庫,它只需找到對應的id,name欄位的列展現出來即可,需要查詢的資料量小,效率高。 假設執行的查詢操作是:select * from table_emp; 對於這種查詢整個表全部資訊的操作,由於列式資料庫需要將分散的行進行重新組合,行式資料庫效率就高於列式資料庫。 但是,在大資料領域,進行全表查詢的場景少之又少,進而我們使用較多的還是列式資料庫及列式儲存。
Hive哪些查詢會執行mr
hive 0.10.0為了執行效率考慮,簡單的查詢,就是隻是select,不帶count,sum,group by這樣的,都不走map/reduce,直接讀取hdfs檔案進行filter過濾。 這樣做的好處就是不新開mr任務,執行效率要提高不少,但是不好的地方就是使用者介面不友好,有時候資料量大還是要等很長時間,但是又沒有任何返回。 改這個很簡單,在hive-site.xml裡面有個配置引數叫 hive.fetch.task.conversion 將這個引數設定為more,簡單查詢就不走map/reduce了,設定為minimal,就任何簡單select都會走map/reduce
Create Table As Select (CTAS) 走mr create table emp2 as select * from emp;
insert一條或者多條 走mr
Hive靜態分割槽動態分割槽
分割槽的概念 Hive的分割槽方式:由於Hive實際是儲存在HDFS上的抽象,Hive的一個分割槽名對應HDFS上的一個目錄名,子分割槽名就是子目錄名,並不是一個實際欄位。 分割槽的好處 產生背景:如果一個表中資料很多,我們查詢時就很慢,耗費大量時間,如果要查詢其中部分資料該怎麼辦呢,這是我們引入分割槽的概念。 Partition:分割槽,每張表中可以加入一個分割槽或者多個,方便查詢,提高效率;並且HDFS上會有對應的分割槽目錄: 語法: Hive分割槽是在建立表的時候用Partitioned by 關鍵字定義的,但要注意,Partitioned by子句中定義的列是表中正式的列, 但是Hive下的資料檔案中並不包含這些列,因為它們是目錄名,真正的資料在分割槽目錄下。 靜態分割槽和 動態分割槽的區別 建立表的語法都一樣
- 靜態分割槽:載入資料的時候要指定分割槽的值(key=value),比較麻煩的是每次插入資料都要指定分割槽的值,建立多個分割槽多分割槽一樣,以逗號分隔。
- 動態分割槽: 如果用上述的靜態分割槽,插入的時候必須首先要知道有什麼分割槽型別,而且每個分割槽寫一個load data,太煩人。使用動態分割槽可解決以上問題,其可以根據查詢得到的資料動態分配到分割槽裡。其實動態分割槽與靜態分割槽區別就是不指定分割槽目錄,由系統自己選擇。
首先,啟動動態分割槽功能
hive> set hive.exec.dynamic.partition=true;
採用動態方式載入資料到目標表
載入之前先設定一下下面的引數
hive (default)> set hive.exec.dynamic.partition.mode=nonstrict
1
開始載入
insert into table emp_dynamic_partition partition(deptno)
select empno , ename , job , mgr , hiredate , sal , comm, deptno from emp;
載入資料方式並沒有指定具體的分割槽,只是指出了分割槽欄位。
在select最後一個欄位必須跟你的分割槽欄位,這樣就會自行根據deptno的value來分割槽。
刪除分割槽:
ALTER TABLE my_partition_test_table DROP IF EXISTS PARTITION (day='2018-08-08');
Hive優化
1.我們知道大資料場景下不害怕資料量大,害怕的是資料傾斜,怎樣避免資料傾斜,找到可能產生資料傾斜的函式尤為關鍵,資料量較大的情況下,慎用count(distinct),count(distinct)容易產生傾斜問題。 2.設定合理的map reduce 的task數量 map階段優化
mapred.min.split.size: 指的是資料的最小分割單元大小;min的預設值是1B
mapred.max.split.size: 指的是資料的最大分割單元大小;max的預設值是256MB
通過調整max可以起到調整map數的作用,減小max可以增加map數,增大max可以減少map數。
需要提醒的是,直接調整mapred.map.tasks這個引數是沒有效果的。
舉例: a) 假設input目錄下有1個檔案a,大小為780M,那麼hadoop會將該檔案a分隔成7個塊(6個128m的塊和1個12m的塊),從而產生7個map數 b) 假設input目錄下有3個檔案a,b,c,大小分別為10m,20m,130m,那麼hadoop會分隔成4個塊(10m,20m,128m,2m),從而產生4個map數 即,如果檔案大於塊大小(128m),那麼會拆分,如果小於塊大小,則把該檔案當成一個塊。
其實這就涉及到小檔案的問題:如果一個任務有很多小檔案(遠遠小於塊大小128m),則每個小檔案也會被當做一個塊,用一個map任務來完成, 而一個map任務啟動和初始化的時間遠遠大於邏輯處理的時間,就會造成很大的資源浪費。 而且,同時可執行的map數是受限的。那麼問題又來了。。是不是保證每個map處理接近128m的檔案塊,就高枕無憂了? 答案也是不一定。比如有一個127m的檔案,正常會用一個map去完成,但這個檔案只有一個或者兩個小欄位,卻有幾千萬的記錄, 如果map處理的邏輯比較複雜,用一個map任務去做,肯定也比較耗時。
我們該如何去解決呢??? 我們需要採取兩種方式來解決:即減少map數和增加map數; - 減少map數量
假設一個SQL任務:
Select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’;
該任務的inputdir /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04
共有194個檔案,其中很多是遠遠小於128m的小檔案,總大小9G,正常執行會用194個map任務。
Map總共消耗的計算資源: SLOTS_MILLIS_MAPS= 623,020
我通過以下方法來在map執行前合併小檔案,減少map數:
set mapred.max.split.size=100000000;
set mapred.min.split.size.per.node=100000000;
set mapred.min.split.size.per.rack=100000000;
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
再執行上面的語句,用了74個map任務,map消耗的計算資源:SLOTS_MILLIS_MAPS= 333,500
對於這個簡單SQL任務,執行時間上可能差不多,但節省了一半的計算資源。
大概解釋一下,100000000表示100M, set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;這個引數表示執行前進行小檔案合併,前面三個引數確定合併檔案塊的大小,大於檔案塊大小128m的,按照128m來分隔,小於128m,大於100m的,按照100m來分隔,把那些小於100m的(包括小檔案和分隔大檔案剩下的),進行合併,最終生成了74個塊。
- 增大map數量
如何適當的增加map數?
當input的檔案都很大,任務邏輯複雜,map執行非常慢的時候,可以考慮增加Map數,
來使得每個map處理的資料量減少,從而提高任務的執行效率。
假設有這樣一個任務:
Select data_desc,
count(1),
count(distinct id),
sum(case when …),
sum(case when ...),
sum(…)
from a group by data_desc
如果表a只有一個檔案,大小為120M,但包含幾千萬的記錄,如果用1個map去完成這個任務,
肯定是比較耗時的,這種情況下,我們要考慮將這一個檔案合理的拆分成多個,
這樣就可以用多個map任務去完成。
set mapred.reduce.tasks=10;
create table a_1 as
select * from a
distribute by rand(123);
這樣會將a表的記錄,隨機的分散到包含10個檔案的a_1表中,再用a_1代替上面sql中的a表,
則會用10個map任務去完成。
每個map任務處理大於12M(幾百萬記錄)的資料,效率肯定會好很多。
看上去,貌似這兩種有些矛盾,一個是要合併小檔案,一個是要把大檔案拆成小檔案,
這點正是重點需要關注的地方,
使單個map任務處理合適的資料量;
reduce階段優化
Reduce的個數對整個作業的執行效能有很大影響。如果Reduce設定的過大,那麼將會產生很多小檔案,
對NameNode會產生一定的影響,
而且整個作業的執行時間未必會減少;如果Reduce設定的過小,那麼單個Reduce處理的資料將會加大,
很可能會引起OOM異常。
如果設定了mapred.reduce.tasks/mapreduce.job.reduces引數,那麼Hive會直接使用它的值作為Reduce的個數;
如果mapred.reduce.tasks/mapreduce.job.reduces的值沒有設定(也就是-1),那麼Hive會
根據輸入檔案的大小估算出Reduce的個數。
根據輸入檔案估算Reduce的個數可能未必很準確,因為Reduce的輸入是Map的輸出,而Map的輸出可能會比輸入要小,
所以最準確的數根據Map的輸出估算Reduce的個數。
1. Hive自己如何確定reduce數: reduce個數的設定極大影響任務執行效率,不指定reduce個數的情況下,Hive會猜測確定一個reduce個數,基於以下兩個設定: hive.exec.reducers.bytes.per.reducer(每個reduce任務處理的資料量,預設為1000^3=1G) hive.exec.reducers.max(每個任務最大的reduce數,預設為999) 計算reducer數的公式很簡單N=min(引數2,總輸入資料量/引數1) 即,如果reduce的輸入(map的輸出)總大小不超過1G,那麼只會有一個reduce任務; 如:select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’ group by pt; /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 總大小為9G多,因此這句有10個reduce
調整reduce個數方法一: 調整hive.exec.reducers.bytes.per.reducer引數的值; set hive.exec.reducers.bytes.per.reducer=500000000; (500M) select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’ group by pt; 這次有20個reduce
調整reduce個數方法二; set mapred.reduce.tasks = 15; select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’ group by pt;這次有15個reduce
reduce個數並不是越多越好; 同map一樣,啟動和初始化reduce也會消耗時間和資源; 另外,有多少個reduce,就會有多少個輸出檔案,如果生成了很多個小檔案,那麼如果這些小檔案作為下一個任務的輸入, 則也會出現小檔案過多的問題;
什麼情況下只有一個reduce; 很多時候你會發現任務中不管資料量多大,不管你有沒有設定調整reduce個數的引數,任務中一直都只有一個reduce任務; 其實只有一個reduce任務的情況,除了資料量小於hive.exec.reducers.bytes.per.reducer引數值的情況外,還有以下原因: a) 沒有group by的彙總,比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’ group by pt; 寫成 select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’; 這點非常常見,希望大家儘量改寫。 b) 用了Order by c) 有笛卡爾積 通常這些情況下,除了找辦法來變通和避免,我暫時沒有什麼好的辦法,因為這些操作都是全域性的,所以hadoop不得不用一個reduce去完成; 同樣的,在設定reduce個數的時候也需要考慮這兩個原則:使大資料量利用合適的reduce數;使單個reduce任務處理合適的資料量;
合併小檔案
我們知道檔案數目小,容易在檔案儲存端造成瓶頸,給 HDFS 帶來壓力,影響處理效率。
對此,可以通過合併Map和Reduce的結果檔案來消除這樣的影響。
用於設定合併屬性的引數有:
是否合併Map輸出檔案:hive.merge.mapfiles=true(預設值為真)
是否合併Reduce 端輸出檔案:hive.merge.mapredfiles=false(預設值為假)
合併檔案的大小:hive.merge.size.per.task=256*1000*1000(預設值為 256000000)
Hive優化之小檔案問題及其解決方案 小檔案是如何產生的 1.動態分割槽插入資料,產生大量的小檔案,從而導致map數量劇增。
2.reduce數量越多,小檔案也越多(reduce的個數和輸出檔案是對應的)。
3.資料來源本身就包含大量的小檔案。
小檔案問題的影響 1.從Hive的角度看,小檔案會開很多map,一個map開一個JVM去執行,所以這些任務的初始化,啟動,執行會浪費大量的資源,嚴重影響效能。
2.在HDFS中,每個小檔案物件約佔150byte,如果小檔案過多會佔用大量記憶體。這樣NameNode記憶體容量嚴重製約了叢集的擴充套件。
小檔案問題的解決方案 從小檔案產生的途經就可以從源頭上控制小檔案數量,方法如下:
1.使用Sequencefile作為表儲存格式,不要用textfile,在一定程度上可以減少小檔案。
2.減少reduce的數量(可以使用引數進行控制)。
3.少用動態分割槽,用時記得按distribute by分割槽。
對於已有的小檔案,我們可以通過以下幾種方案解決:
1.使用hadoop archive命令把小檔案進行歸檔。
2.重建表,建表時減少reduce數量。
3.通過引數進行調節,設定map/reduce端的相關引數,如下:
設定map輸入合併小檔案的相關引數:
[java] view plain copy //每個Map最大輸入大小(這個值決定了合併後文件的數量) set mapred.max.split.size=256000000; //一個節點上split的至少的大小(這個值決定了多個DataNode上的檔案是否需要合併) set mapred.min.split.size.per.node=100000000; //一個交換機下split的至少的大小(這個值決定了多個交換機上的檔案是否需要合併) set mapred.min.split.size.per.rack=100000000; //執行Map前進行小檔案合併 set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
設定map輸出和reduce輸出進行合併的相關引數: [java] view plain copy //設定map端輸出進行合併,預設為true set hive.merge.mapfiles = true //設定reduce端輸出進行合併,預設為false set hive.merge.mapredfiles = true //設定合併檔案的大小 set hive.merge.size.per.task = 256*1000*1000 //當輸出檔案的平均大小小於該值時,啟動一個獨立的MapReduce任務進行檔案merge。 set hive.merge.smallfiles.avgsize=16000000
3.Write good SQL :說道sql優化很慚愧,自己sql很爛,不多比比了,但是sql優化確實很關鍵。。。 4.儲存格式:可以使用列裁剪,分割槽裁剪,orc,parquet等儲存格式。參考該部落格
Hive支援ORCfile,這是一種新的表格儲存格式,通過諸如謂詞下推,壓縮等技術來提高執行速度提升。
對於每個HIVE表使用ORCFile應該是一件容易的事情,並且對於獲得HIVE查詢的快速響應時間非常有益。
作為一個例子,考慮兩個大表A和B(作為文字檔案儲存,其中一些列未在此處指定,即行試儲存的缺點)以及一個簡單的查詢,如:
SELECT A.customerID, A.name, A.age, A.address join
B.role, B.department, B.salary
ON A.customerID=B.customerID;
此查詢可能需要很長時間才能執行,因為表A和B都以TEXT形式儲存,進行全表掃描。
將這些表格轉換為ORCFile格式通常會顯著減少查詢時間:
ORC支援壓縮儲存(使用ZLIB或如上所示使用SNAPPY),但也支援未壓縮的儲存。
CREATE TABLE A_ORC (
customerID int, name string, age int, address string
) STORED AS ORC tblproperties (“orc.compress" = “SNAPPY”);
INSERT INTO TABLE A_ORC SELECT * FROM A;
CREATE TABLE B_ORC (
customerID int, role string, salary float, department string
) STORED AS ORC tblproperties (“orc.compress" = “SNAPPY”);
INSERT INTO TABLE B_ORC SELECT * FROM B;
SELECT A_ORC.customerID, A_ORC.name,
A_ORC.age, A_ORC.address join
B_ORC.role, B_ORC.department, B_ORC.salary
ON A_ORC.customerID=B_ORC.customerID;
5.壓縮格式:大資料場景下儲存格式壓縮格式尤為關鍵,可以提升計算速度,減少儲存空間,降低網路io,磁碟io,所以要選擇合適的壓縮格式和儲存格式,那麼首先就瞭解這些東西,作者以前部落格已經進行了詳細的說明,參考該部落格 6.MAP JOIN MapJoin簡單說就是在Map階段將小表讀入記憶體,順序掃描大表完成Join。 上圖是Hive MapJoin的原理圖,出自Facebook工程師Liyin Tang的一篇介紹Join優化的slice,從圖中可以看出MapJoin分為兩個階段: (1)通過MapReduce Local Task,將小表讀入記憶體,生成HashTableFiles上傳至Distributed Cache中,這裡會對HashTableFiles進行壓縮。 (2)MapReduce Job在Map階段,每個Mapper從Distributed Cache讀取HashTableFiles到記憶體中,順序掃描大表,在Map階段直接進行Join,將資料傳遞給下一個MapReduce任務。 也就是在map端進行join避免了shuffle。 7.引擎的選擇
Hive可以使用ApacheTez執行引擎而不是古老的Map-Reduce引擎。
我不會詳細討論在這裡提到的使用Tez的許多好處; 相反,我想提出一個簡單的建議:
如果它沒有在您的環境中預設開啟,請在您的Hive查詢的開頭將以下內容設定為'true'來使用Tez:
設定hive.execution.engine = tez;
通過上述設定,您執行的每個HIVE查詢都將利用Tez。
目前Hive On Spark還處於試驗階段,慎用。。
8.Use Vectorization
向量化查詢執行通過一次性批量執行1024行而不是每次單行執行,從而提高掃描,聚合,篩選器和連線等操作的效能。
在Hive 0.13中引入,此功能顯著提高了查詢執行時間,並可通過兩個引數設定輕鬆啟用:
設定hive.vectorized.execution.enabled = true;
設定hive.vectorized.execution.reduce.enabled = true;
9.cost based query optimization
Hive 自0.14.0開始,加入了一項”Cost based Optimizer”來對HQL執行計劃進行優化,這個功能通
過”hive.cbo.enable”來開啟。在Hive 1.1.0之後,這個feature是預設開啟的,它可以自動優化HQL中多個JOIN的順序,並
選擇合適的JOIN演算法.
Hive在提交最終執行前,優化每個查詢的執行邏輯和物理執行計劃。這些優化工作是交給底層來完成。
根據查詢成本執行進一步的優化,從而產生潛在的不同決策:如何排序連線,執行哪種型別的連線,並行度等等。
要使用基於成本的優化(也稱為CBO),請在查詢開始處設定以下引數:
設定hive.cbo.enable = true;
設定hive.compute.query.using.stats = true;
設定hive.stats.fetch.column.stats = true;
設定hive.stats.fetch.partition.stats = true;