Hive的調優你都知道那些?

         咱們在工做中仍是在學習中有都會遇到咱們寫的HQL語句執行效率不高,那咱們該怎麼提升查詢效率那,這篇文章就帶你從不一樣維度講解,讓你的HQL瞬間提升一個檔次。記得收藏
node

1、Fetch抓取(Hive能夠避免進行MapReduce)

         Hive中對某些狀況的查詢能夠沒必要使用MapReduce計算。例如:SELECT * FROM employees;在這種狀況下,Hive能夠簡單地讀取employee對應的存儲目錄下的文件,而後輸出查詢結果到控制檯。
         在hive-default.xml.template文件中hive.fetch.task.conversion默認是more,老版本hive默認是minimal,該屬性修改成more之後,在全局查找、字段查找、limit查找等都不走mapreducelinux

<property>
    <name>hive.fetch.task.conversion</name>
    <value>more</value>
    <description>
      Expects one of [none, minimal, more].
      Some select queries can be converted to single FETCH task minimizing latency.
      Currently the query should be single sourced not having any subquery and should not have
      any aggregations or distincts (which incurs RS), lateral views and joins.
      0. none : disable hive.fetch.task.conversion
    </description>
  </property>

1.1 案例實操

  1. 把hive.fetch.task.conversion設置成none,而後執行查詢語句,都會執行mapreduce程序。
hive (default)> set hive.fetch.task.conversion=none;
hive (default)> select * from score;
hive (default)> select s_score from score;
hive (default)> select s_score from score limit 3;
  1. 把hive.fetch.task.conversion設置成more,而後執行查詢語句,以下查詢方式都不會執行mapreduce程序

1.2 本地模式

         大多數的Hadoop Job是須要Hadoop提供的完整的可擴展性來處理大數據集的。不過,有時Hive的輸入數據量是很是小的。在這種狀況下,爲查詢觸發執行任務時消耗可能會比實際job的執行時間要多的多。對於大多數這種狀況,Hive能夠經過本地模式在單臺機器上處理全部的任務。對於小數據集,使用本地模式執行時間能夠明顯被縮短
         用戶能夠經過設置hive.exec.mode.local.auto的值爲true,來讓Hive在適當的時候自動啓動這個優化git

set hive.exec.mode.local.auto=true;  --開啓本地MapReduce
-- 設置local mr的最大輸入數據量,當輸入數據量小於這個值時採用local mr的方式,默認爲134217728,即128M
set hive.exec.mode.local.auto.inputbytes.max=51234560;
-- 設置local mr的最大輸入文件個數,當輸入文件個數小於這個值時採用local mr的方式,默認爲4
set hive.exec.mode.local.auto.input.files.max=10

實操案例
開啓本地模式,並執行查詢語句程序員

hive (default)> set hive.exec.mode.local.auto=true; 
hive (default)> select * from score cluster by s_id;
1.568 seconds

關閉本地模式,並執行查詢語句github

hive (default)> set hive.exec.mode.local.auto=false; 
hive (default)> select * from score cluster by s_id;
11.865 seconds

2、group By

         默認狀況下,Map階段同一Key數據分發給一個reduce,當一個key數據過大時就傾斜了
         並非全部的聚合操做都須要在Reduce端完成,不少聚合操做均可以先在Map端進行部分聚合,最後在Reduce端得出最終結果面試

開啓Map端聚合參數設置sql

-- 是否在Map端進行聚合,默認爲True
set hive.map.aggr = true;
--在Map端進行聚合操做的條目數目
set hive.groupby.mapaggr.checkinterval = 100000;
-- 有數據傾斜的時候進行負載均衡(默認是false)
set hive.groupby.skewindata = true;

         當選項設定爲 true,生成的查詢計劃會有兩個MR Job。第一個MR Job中,Map的輸出結果會隨機分佈到Reduce中,每一個Reduce作部分聚合操做,並輸出結果,這樣處理的結果是相同的Group By Key有可能被分發到不一樣的Reduce中,從而達到負載均衡的目的;第二個MR Job再根據預處理的數據結果按照Group By Key分佈到Reduce中(這個過程能夠保證相同的Group By Key被分佈到同一個Reduce中),最後完成最終的聚合操做。數據庫

3、Count(distinct)

         數據量小的時候無所謂,數據量大的狀況下,因爲COUNT DISTINCT操做須要用一個Reduce Task來完成,這一個Reduce須要處理的數據量太大,就會致使整個Job很難完成,通常COUNT DISTINCT使用先GROUP BY再COUNT的方式替換數據結構

環境準備併發

create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

load data local inpath '/home/bigtable' into table bigtable;

使用count(distinct)

set hive.exec.reducers.bytes.per.reducer=32123456;
SELECT count(DISTINCT id) FROM bigtable;
結果:
c0
10000
Time taken: 35.49 seconds, Fetched: 1 row(s)

使用轉換

set hive.exec.reducers.bytes.per.reducer=32123456;
SELECT count(id) FROM (SELECT id FROM bigtable GROUP BY id) a;
結果:
Stage-Stage-1: Map: 1  Reduce: 4   Cumulative CPU: 13.07 sec   HDFS Read: 120749896 HDFS Write: 464 SUCCESS
Stage-Stage-2: Map: 3  Reduce: 1   Cumulative CPU: 5.14 sec   HDFS Read: 8987 HDFS Write: 7 SUCCESS
_c0
10000
Time taken: 51.202 seconds, Fetched: 1 row(s)

雖然會多用一個Job來完成,但在數據量大的狀況下,這個絕對是值得的。

測試數據下載 連接:https://pan.baidu.com/s/1LwKKJTeXR4h0iaOAknZ7_g 提取碼:5252

4、笛卡爾積

         儘可能避免笛卡爾積,即避免join的時候不加on條件,或者無效的on條件,Hive只能使用1個reducer來完成笛卡爾積。

5、使用分區剪裁、列剪裁

         在SELECT中,只拿須要的列,若是有,儘可能使用分區過濾,少用SELECT *。
         在分區剪裁中,當使用外關聯時,若是將副表的過濾條件寫在Where後面,那麼就會先全表關聯,以後再過濾,好比:

數據準備

create table ori(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';
create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';
load data local inpath '/home/bigtable' into table bigtable;
load data local inpath '/home/ori' into table ori;

先關聯在Where

select a.id FROM bigtable a left join ori o on a.id=o.id where o.id<=10;

正確寫法是在ON後面: 向Where 在關聯

select a.id FROM bigtable a left join ori o  on (o.id<=10 and a.id=o.id);

或者直接寫成子查詢

select a.id from bigtable a join (select  id from ori o where o.id<=10) o on o.id=a.id;

測試數據下載 連接:https://pan.baidu.com/s/1LwKKJTeXR4h0iaOAknZ7_g 提取碼:5252

6、 動態分區調整

         關係型數據庫中,對分區表Insert數據時候,數據庫自動會根據分區字段的值,將數據插入到相應的分區中,Hive中也提供了相似的機制,即動態分區(Dynamic Partition),只不過,使用Hive的動態分區,須要進行相應的配置。
         以第一個表的分區規則,來對應第二個表的分區規則,將第一個表的全部分區,所有拷貝到第二個表中來,第二個表在加載數據的時候,不須要指定分區了,直接用第一個表的分區便可。
開啓動態分區參數設置

  1. 開啓動態分區參數設置
set hive.exec.dynamic.partition=true;
  1. 設置爲非嚴格模式(動態分區的模式,默認strict,表示必須指定至少一個分區爲靜態分區,nonstrict模式表示容許全部的分區字段均可以使用動態分區。)
set hive.exec.dynamic.partition.mode=nonstrict;
  1. 在全部執行MR的節點上,最大一共能夠建立多少個動態分區。
set  hive.exec.max.dynamic.partitions=1000;
  1. 在每一個執行MR的節點上,最大能夠建立多少個動態分區。該參數須要根據實際的數據來設定。好比:源數據中包含了一年的數據,即day字段有365個值,那麼該參數就須要設置成大於365,若是使用默認值100,則會報錯。
set hive.exec.max.dynamic.partitions.pernode=100
  1. 整個MR Job中,最大能夠建立多少個HDFS文件。
             在linux系統當中,每一個linux用戶最多能夠開啓1024個進程,每個進程最多能夠打開2048個文件,即持有2048個文件句柄,下面這個值越大,就能夠打開文件句柄越大
set hive.exec.max.created.files=100000
  1. 當有空分區生成時,是否拋出異常。通常不須要設置。
set hive.error.on.empty.partition=false

案例實操

需求: 將ori中的數據按照時間(如:20111231234568),插入到目標表ori_partitioned的相應分區中。

  1. 準備數據表
create table ori_partitioned(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string)
PARTITIONED BY (p_time bigint)
row format delimited fields terminated by '\t';
load data local inpath '/home/small_data' into  table ori_partitioned partition (p_time='20111230000010');
load data local inpath '/home/small_data' into  table ori_partitioned partition (p_time='20111230000011');
  1. 建立分區表
create table ori_partitioned_target(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) PARTITIONED BY (p_time STRING) row format delimited fields terminated by '\t';
  1. 分析
             若是按照以前介紹的往指定一個分區中Insert數據,那麼這個需求很不容易實現。這時候就須要使用動態分區來實現
set hive.exec.dynamic.partition = true;
set hive.exec.dynamic.partition.mode = nonstrict;
set hive.exec.max.dynamic.partitions = 1000;
set hive.exec.max.dynamic.partitions.pernode = 100;
set hive.exec.max.created.files = 100000;
set hive.error.on.empty.partition = false;
INSERT overwrite TABLE ori_partitioned_target PARTITION (p_time) SELECT id,`time`, uid, keyword, url_rank, click_num, click_url, p_time FROM ori_partitioned;

         注意:在PARTITION (p_time)中指定分區字段名便可;在SELECT子句的最後幾個字段,必須對應前面PARTITION (p_time)中指定的分區字段,包括順序。

查看分區

hive (default)>  show partitions ori_partitioned_target;
OK
partition
p_time=20111230000010
p_time=20111230000011
Time taken: 0.607 seconds, Fetched: 2 row(s)

測試數據下載 連接:https://pan.baidu.com/s/1LwKKJTeXR4h0iaOAknZ7_g 提取碼:5252

7、數據傾斜

7.1 Map數

         一般狀況下,做業會經過input的目錄產生一個或者多個map任務
         主要的決定因素有:input的文件總個數input的文件大小,集羣設置的文件塊大小(目前爲128M,可在hive中經過set dfs.block.size;命令查看到,該參數不能自定義修改)。

舉例:

  • 一個大文件:假設input目錄下有1個文件a,大小爲780M,那麼hadoop會將該文件a分隔成7個塊6個128m的塊和1個12m的塊),從而產生7個map數。
  • 多個小文件 :假設input目錄下有3個文件a,b,c大小分別爲10m,20m,150m,那麼hadoop會分隔成4個塊10m,20m,128m,22m),從而產生4個map數。即,若是文件大於塊大小(128m),那麼會拆分,若是小於塊大小,則把該文件當成一個塊。
  • 是否是map數越多越好?答案是否認的。若是一個任務有不少小文件(遠遠小於塊大小128m),則每一個小文件也會被當作一個塊,用一個map任務來完成,而一個map任務啓動和初始化的時間遠遠大於邏輯處理的時間,就會形成很大的資源浪費。並且,同時可執行的map數是受限的。
  • 是否是保證每一個map處理接近128m的文件塊,就高枕無憂了?答案也是不必定。好比有一個127m的文件,正常會用一個map去完成,但這個文件只有一個或者兩個字段,卻有幾千萬的記錄,若是map處理的邏輯比較複雜,用一個map任務去作,確定也比較耗時
  • 小結: 針對上面的問題3和4,咱們須要採起兩種方式來解決:即減小map數和增長map數;這個就看看真實的場景去確認map的數量。

7.2 如何適當的增長map數

         當input的文件都很大,任務邏輯複雜,map執行很是慢的時候,能夠考慮增長Map數,來使得每一個map處理的數據量減小,從而提升任務的執行效率。

針對上面的第4條

假設有這樣一個任務:

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 mapreduce.job.reduces =10;
create table a_1 as
select * from a
distribute by rand();

         這樣會將a表的記錄,隨機的分散到包含10個文件的a_1表中,再用a_1代替上面sql中的a表,則會用10個map任務去完成。
         每一個map任務處理大於12M(幾百萬記錄)的數據,效率確定會好不少。
         看上去,貌似這兩種有些矛盾,一個是要合併小文件,一個是要把大文件拆成小文件,這點正是重點須要關注的地方,根據實際狀況,控制map數量須要遵循兩個原則:使大數據量利用合適的map數;使單個map任務處理合適的數據量

7.3 reduce數

調整reduce個數方法一

  • 每一個Reduce處理的數據量默認是256MB
hive.exec.reducers.bytes.per.reducer=256123456
  • 計算reducer數的公式
hive.exec.reducers.max=1009

調整reduce個數方法二

  • 設置每一個job的Reduce個數
set mapreduce.job.reduces = 15;

reduce個數並非越多越好

  • 過多的啓動和初始化reduce也會消耗時間和資源;
  • 另外,有多少個reduce,就會有多少個輸出文件,若是生成了不少個小文件,那麼若是這些小文件做爲下一個任務的輸入,則也會出現小文件過多的問題;

         在設置reduce個數的時候也須要考慮這兩個原則:處理大數據量利用合適的reduce數;使單個reduce任務處理數據量大小要合適

8、並行執行

         Hive會將一個查詢轉化成一個或者多個階段。這樣的階段可使MapReduce階段、抽樣階段、合併階段、limit階段。或者Hive執行過程當中可能須要的其餘階段。默認狀況下,Hive一次只會執行一個階段。不過,某個特定的job可能包含衆多的階段,而這些階段可能並不是徹底互相依賴的,也就是說有些階段是能夠並行執行的,這樣可能使得整個job的執行時間縮短。不過,若是有更多的階段能夠並行執行,那麼job可能就越快完成。
         經過設置參數hive.exec.parallel值爲true,就能夠開啓併發執行。不過,在共享集羣中,須要注意下,若是job中並行階段增多,那麼集羣利用率就會增長。

set hive.exec.parallel=true;     //打開任務並行執行
set hive.exec.parallel.thread.number=16; //同一個sql容許最大並行度,默認爲8。

         固然,得是在系統資源比較空閒的時候纔有優點,不然,沒資源,並行也起不來。

9、嚴格模式

         Hive提供了一個嚴格模式,能夠防止用戶執行「高危」的查詢
         經過設置屬性hive.mapred.mode值爲默認是非嚴格模式nonstrict 。開啓嚴格模式須要修改hive.mapred.mode值爲strict,開啓嚴格模式能夠禁止3種類型的查詢。

<property>
    <name>hive.mapred.mode</name>
    <value>strict</value>
    <description>
      The mode in which the Hive operations are being performed. 
      In strict mode, some risky queries are not allowed to run. They include:
        Cartesian Product.
        No partition being picked up for a query.
        Comparing bigints and strings.
        Comparing bigints and doubles.
        Orderby without limit.
    </description>
  </property>
  1. 對於分區表,用戶不容許掃描全部分區,除非where語句中含有分區字段過濾條件來限制範圍,不然不容許執行。進行這個限制的緣由是,一般分區表都擁有很是大的數據集,並且數據增長迅速。沒有進行分區限制的查詢可能會消耗使人不可接受的巨大資源來處理這個表。
  2. 對於使用了order by語句的查詢,要求必須使用limit語句。由於order by爲了執行排序過程會將全部的結果數據分發到同一個Reducer中進行處理,強制要求用戶增長這個LIMIT語句能夠防止Reducer額外執行很長一段時間
  3. 限制笛卡爾積的查詢。對關係型數據庫很是瞭解的用戶可能指望在執行JOIN查詢的時候不使用ON語句而是使用where語句這樣關係數據庫的執行優化器就能夠高效地將WHERE語句轉化成那個ON語句。\不幸的是,Hive並不會執行這種優化,所以,若是表足夠大,那麼這個查詢就會出現不可控的狀況。

10、 JVM重用

         JVM重用是Hadoop調優參數的內容,其對Hive的性能具備很是大的影響,特別是對於很難避免小文件的場景或task特別多的場景,這類場景大多數執行時間都很短。
         Hadoop的默認配置一般是使用派生JVM來執行map和Reduce任務的。這時JVM的啓動過程可能會形成至關大的開銷,尤爲是執行的job包含有成百上千task任務的狀況。JVM重用可使得JVM實例在同一個job中從新使用N次。N的值能夠在Hadoop的mapred-site.xml文件中進行配置。一般在10-20之間,具體多少須要根據具體業務場景測試得出。

<property>
  <name>mapreduce.job.jvm.numtasks</name>
  <value>10</value>
  <description>How many tasks to run per jvm. If set to -1, there is
  no limit. 
  </description>
</property>

         咱們也能夠在hive當中經過

set  mapred.job.reuse.jvm.num.tasks=10;

         這個設置來設置咱們的jvm重用

         這個功能的缺點是,開啓JVM重用將一直佔用使用到的task插槽,以便進行重用,直到任務完成後才能釋放。若是某個「不平衡的」job中有某幾個reduce task執行的時間要比其餘Reduce task消耗的時間多的多的話,那麼保留的插槽就會一直空閒着卻沒法被其餘的job使用,直到全部的task都結束了纔會釋放。

11、推測執行

         在分佈式集羣環境下,由於程序Bug(包括Hadoop自己的bug),負載不均衡或者資源分佈不均等緣由,會形成同一個做業的多個任務之間運行速度不一致,有些任務的運行速度可能明顯慢於其餘任務(好比一個做業的某個任務進度只有50%,而其餘全部任務已經運行完畢),則這些任務會拖慢做業的總體執行進度。爲了不這種狀況發生,Hadoop採用了推測執行(Speculative Execution)機制,它根據必定的法則推測出「拖後腿」的任務,併爲這樣的任務啓動一個備份任務,讓該任務與原始任務同時處理同一份數據,並最終選用最早成功運行完成任務的計算結果做爲最終結果。
Hive 一樣能夠開啓推測執行
設置開啓推測執行參數:Hadoop的mapred-site.xml文件中進行配置

<property>
  <name>mapreduce.map.speculative</name>
  <value>true</value>
  <description>If true, then multiple instances of some map tasks  may be executed in parallel.</description>
</property>

<property>
  <name>mapreduce.reduce.speculative</name>
  <value>true</value>
  <description>If true, then multiple instances of some reduce tasks   may be executed in parallel.</description>
</property>

不過hive自己也提供了配置項來控制reduce-side的推測執行:

<property>
    <name>hive.mapred.reduce.tasks.speculative.execution</name>
    <value>true</value>
    <description>Whether speculative execution for reducers should be turned on. </description>
  </property>

         關於調優這些推測執行變量,還很難給一個具體的建議。若是用戶對於運行時的誤差很是敏感的話,那麼能夠將這些功能關閉掉。若是用戶由於輸入數據量很大而須要執行長時間的map或者Reduce task的話,那麼啓動推測執行形成的浪費是很是巨大大。

12、表的優化

12.1 Join

Join 原則

  1. 小表Join大表
             將key相對分散,而且數據量小的表放在join的左邊,這樣能夠有效減小內存溢出錯誤發生的概率;再進一步,可使用Group讓小的維度表(1000條如下的記錄條數)先進內存。在map端完成reduce。
select  count(distinct s_id)  from score;
select count(s_id) from score group by s_id; -- 在map端進行聚合,效率更高x`
  1. 多個表關聯時,最好分拆成小段,避免大sql(沒法控制中間Job)
  2. 大表Join大表
    (1) 空KEY過濾

         有時join超時是由於某些key對應的數據太多,而相同key對應的數據都會發送到相同的reducer上,從而致使內存不夠。此時咱們應該仔細分析這些異常的key,不少狀況下,這些key對應的數據是異常數據,咱們須要在SQL語句中進行過濾。
例如key對應的字段爲空,操做以下:

create table ori(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table nullidtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table jointable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

load data local inpath '/home/hive_big_table/*' into table ori;
load data local inpath '/home/hive_have_null_id/*' into table nullidtable;

不過濾運行結果

INSERT OVERWRITE TABLE jointable SELECT a.* FROM nullidtable a JOIN ori b ON a.id = b.id;
Time taken: 129.416 seconds

過濾運行結果

INSERT OVERWRITE TABLE jointable SELECT a.* FROM (SELECT * FROM nullidtable WHERE id IS NOT NULL ) a JOIN ori b ON a.id = b.id;
Time taken: 116.691 seconds

         (2)空key轉換
         有時雖然某個key爲空對應的數據不少,可是相應的數據不是異常數據,必需要包含在join的結果中,此時咱們能夠表a中key爲空的字段賦一個隨機的值,使得數據隨機均勻地分不到不一樣的reducer上。例如:
         不隨機分佈:

set hive.exec.reducers.bytes.per.reducer=32123456;
set mapreduce.job.reduces=7;
INSERT OVERWRITE TABLE jointable SELECT a.* FROM nullidtable a LEFT JOIN ori b ON  CASE WHEN  a.id IS NULL  THEN 'hive'  ELSE a.id  END;
Time taken: 42.594  seconds

         結果:這樣的後果就是全部爲null值的id所有都變成了相同的字符串「hive」,及其容易形成數據的傾斜(全部的key相同,相同key的數據會到同一個reduce當中去)

         爲了解決這種狀況,咱們能夠經過hive的rand函數,隨記的給每個爲空的id賦上一個隨機值,這樣就不會形成數據傾斜
隨機分佈:

set hive.exec.reducers.bytes.per.reducer=32123456;
set mapreduce.job.reduces=7;
INSERT OVERWRITE TABLE jointable SELECT a.* FROM nullidtable a LEFT JOIN ori b ON  CASE WHEN  a.id IS NULL  THEN concat('hive',rand())  ELSE a.id  END;
Time taken: 42.594  seconds

案例實操
需求:測試大表JOIN小表和小表JOIN大表的效率 (新的版本當中已經沒有區別了,舊的版本當中須要使用小表

  1. 建大表、小表和JOIN後表的語句
create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table smalltable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

create table jointable2(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';
  1. 分別向大表小表導入數據
load data local inpath '/home/bigtable' into table bigtable;
 load data local inpath '/home/small_data' into table smalltable;
  1. 關閉mapjoin功能(默認是打開的)
set hive.auto.convert.join = false;
  1. 執行小表JOIN大表語句
INSERT OVERWRITE TABLE jointable2
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
FROM smalltable s
left JOIN bigtable  b
ON b.id = s.id;
Time taken: 30.133 seconds
  1. 執行大表JOIN小表語句
INSERT OVERWRITE TABLE jointable2
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
FROM bigtable  b
left JOIN smalltable  s
ON s.id = b.id;
OK
b.id    b.time  b.uid   b.keyword       b.url_rank      b.click_num     b.click_url
Time taken: 25.116 seconds

         能夠看出大表join小表或者小表join大表,就算是關閉map端join的狀況下,在新的版本當中基本上沒有區別了(hive爲了解決數據傾斜的問題,會自動進行過濾)

12.2 MapJoin

         若是不指定MapJoin或者不符合MapJoin的條件,那麼Hive解析器會將Join操做轉換成Common Join(在Reduce階段完成join)。容易發生數據傾斜。能夠用MapJoin把小表所有加載到內存在map端進行join,避免reducer處理。

開啓MapJoin參數設置:

  1. 設置自動選擇Mapjoin
set hive.auto.convert.join = true; 默認爲true
  1. 大表小表的閾值設置(默認25M如下認爲是小表):
set hive.mapjoin.smalltable.filesize=25123456;

MapJoin工做機制
在這裏插入圖片描述
         首先是Task A,它是一個Local Task(在客戶端本地執行的Task),負責掃描小表b的數據,將其轉換成一個HashTable的數據結構,並寫入本地的文件中,以後將該文件加載到DistributeCache中。
         接下來是Task B,該任務是一個沒有Reduce的MR,啓動MapTasks掃描大表a,在Map階段,根據a的每一條記錄去和DistributeCache中b表對應的HashTable關聯,並直接輸出結果。
         因爲MapJoin沒有Reduce,因此由Map直接輸出結果文件,有多少個Map Task,就有多少個結果文件。
案例實操:

  1. 開啓Mapjoin功能
set hive.auto.convert.join = true; -- 默認爲true
  1. 執行小表JOIN大表語句
INSERT OVERWRITE TABLE jointable2
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
FROM smalltable s
JOIN bigtable  b
ON s.id = b.id;
OK
b.id    b.time  b.uid   b.keyword       b.url_rank      b.click_num     b.click_url
Time taken: 18.057 seconds

3.執行大表JOIN小表語句

INSERT OVERWRITE TABLE jointable2
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url
FROM smalltable s
JOIN bigtable  b
ON s.id = b.id;
OK
b.id    b.time  b.uid   b.keyword       b.url_rank      b.click_num     b.click_url
Time taken: 12.822 seconds

測試數據下載 連接:https://pan.baidu.com/s/1LwKKJTeXR4h0iaOAknZ7_g 提取碼:5252

總結

         今天分享的內容有點多,滿滿的乾貨,其實咱們在工做中寫出HQL還有很大的優化,當你從執行一分鐘的SQL語句優化到10秒鐘的的時候一種什麼體驗?信本身,努力和汗水總會能獲得回報的。我是大數據老哥,咱們下期見~~~

資源獲取 獲取Flink面試題,Spark面試題,程序員必備軟件,hive面試題,Hadoop面試題,Docker面試題,簡歷模板等資源請去GitHub自行下載 https://github.com/lhh2002/Framework-Of-BigData

相關文章
相關標籤/搜索