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集羣動態添加更多機器擴展),性能,可擴展性,
容錯性以及與其輸入格式的鬆散耦合。
1
2
3
4
5
6
7
安裝部署

安裝部署這裏我們就不講解了,不會的同學,參考作者以前的博客

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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Hive的優點

1.簡單易上手
2.擴展能力較好(指集羣 HDFS或是YARN)
3.統一的元數據管理 metastore包括的了數據庫,表,字段分區等詳細信息
1
2
3
該篇博客對於元數據信息進行了詳細的講解

4.由於統一的元數據管理所以和spark/impala等SQL引擎是通用的 
通用是指,在擁有了統一的metastore之後,在Hive中創建一張表,在Spark/impala中是能用的,反之在Spark中創建一張表,
在Hive中也能用;只需要共用元數據,就可以切換SQL引擎   
涉及到了Spark sql 和Hive On Spark(實驗版本)
5.使用SQL語法,提供快速開發的能力,支持自定義函數UDF。
6.避免了去寫mapreduce,減少開發人員學習成本。
7.數據離線處理,比如日誌分析,海量數據結構化分析
1
2
3
4
5
6
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
加載數據方式並沒有指定具體的分區,只是指出了分區字段。 
在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這個參數是沒有效果的。
1
2
3
4
舉例: 
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個塊。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
增大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任務處理合適的數據量;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
2
3
4
5
6
7
8
9
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)
1
2
3
4
5
6
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格式通常會顯着減少查詢時間:
1
2
3
4
5
6
7
8
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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還處於試驗階段,慎用。。
1
2
3
4
5
6
8.Use Vectorization

向量化查詢執行通過一次性批量執行1024行而不是每次單行執行,從而提高掃描,聚合,篩選器和連接等操作的性能。
在Hive 0.13中引入,此功能顯着提高了查詢執行時間,並可通過兩個參數設置輕鬆啓用:
設置hive.vectorized.execution.enabled = true;
設置hive.vectorized.execution.reduce.enabled = true;
1
2
3
4
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
10.模式選擇

本地模式 
對於大多數情況,Hive可以通過本地模式在單臺機器上處理所有任務。 
對於小數據,執行時間可以明顯被縮短。通過set hive.exec.mode.local.auto=true(默認爲false)設置本地模式。 
hive> set hive.exec.mode.local.auto; 
hive.exec.mode.local.auto=false

並行模式 
Hive會將一個查詢轉化成一個或者多個階段。這樣的階段可以是MapReduce階段、抽樣階段、合併階段、limit階段。 
默認情況下,Hive一次只會執行一個階段,由於job包含多個階段,而這些階段並非完全互相依賴, 
即:這些階段可以並行執行,可以縮短整個job的執行時間。設置參數:set hive.exec.parallel=true,或者通過配置文件來完成。 
hive> set hive.exec.parallel; 
hive.exec.parallel=false

嚴格模式 
Hive提供一個嚴格模式,可以防止用戶執行那些可能產生意想不到的影響查詢,通過設置 
Hive.mapred.modestrict來完成 
set Hive.mapred.modestrict; 
Hive.mapred.modestrict is undefined

11.JVM重用 
Hadoop通常是使用派生JVM來執行map和reduce任務的。這時JVM的啓動過程可能會造成相當大的開銷, 
尤其是執行的job包含偶成百上千的task任務的情況。JVM重用可以使得JVM示例在同一個job中時候使用N此。 
通過參數mapred.job.reuse.jvm.num.tasks來設置。

12.推測執行 
Hadoop推測執行可以觸發執行一些重複的任務,儘管因對重複的數據進行計算而導致消耗更多的計算資源, 
不過這個功能的目標是通過加快獲取單個task的結果以偵測執行慢的TaskTracker加入到沒名單的方式來提高整體的任務執行效率。

Hadoop的推測執行功能由2個配置控制着,通過mapred-site.xml中配置

mapred.map.tasks.speculative.execution=true

mapred.reduce.tasks.speculative.execution=true
--------------------- 
 
原文:https://blog.csdn.net/yu0_zhang0/article/details/81776459 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章