開發中hive常見的調優策略

牛大財有大才發表於2018-03-19

優化一:開啟本地模式

       大多數的Hadoop Job是需要Hadoop提供的完整的可擴充套件性來處理大資料集的。不過,有時Hive的輸入資料量是非常小的。在這種情況下,為查詢觸發執行任務消耗的時間可能會比實際job的執行時間要多的多。對於大多數這種情況,Hive可以通過本地模式在單臺機器上處理所有的任務。對於小資料集,執行時間可以明顯被縮短。

       如果是單次任務開啟本地模式的話,那麼可以直接在命令列裡執行下面set語句,然後再執行hql語句。如果當前使用者想將自己的任務啟動本地模式,則可以將下面的配置寫到$HOME/.hiverc檔案裡(因為每次hive啟動都會去載入執行.hiverc裡的命令。)如果希望所有的使用者都使用這個配置,那麼將下面的配置直接寫到hive-site.xml檔案裡即可。

set hive.exec.mode.local.auto=true;  //開啟本地mr,預設是關閉false

set hive.exec.mode.local.auto.inputbytes.max=50000000;

//設定local mr的最大輸入資料量,當輸入資料量小於這個值時採用local  mr的方式,預設為134217728,即128M。現在設定其值為50000000,不必為128Mb的整數倍

set hive.exec.mode.local.auto.input.files.max=10;

//設定local mr的最大輸入檔案個數,當輸入檔案個數小於這個值時採用local mr的方式,預設為4

優化二:開啟嚴格模式

         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並不會執行這種優化,因此,如果表足夠大,那麼這個查詢就會出現不可控的情況

優化三:Fetch抓取

        Fetch抓取是指,Hive中對某些情況的查詢可以不必使用MapReduce計算。例如:SELECT * FROM employees;在這種情況下,Hive可以簡單地讀取employee對應的儲存目錄下的檔案,然後輸出查詢結果到控制檯。

        在hive-default.xml.template檔案中hive.fetch.task.conversion預設是more,老版本hive預設是minimal,該屬性修改為more以後,在全域性查詢、欄位查詢、limit查詢等都不走mapreduce

<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

      1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only

      2. more  : SELECT, FILTER, LIMIT only (support TABLESAMPLE and virtual columns)

    </description>

  </property>

實操 1:把hive.fetch.task.conversion設定成more,然後執行查詢語句,都會執行mapreduce程式。

    hive (fdm_sor)>set hive.fetch.task.conversion=more;

    hive (fdm_sor)>select * from SOR_EVT_TB_REPAY_PLAN_HIS;

    hive (fdm_sor)>select name,account  from SOR_EVT_TB_REPAY_PLAN_HIS;

    hive (fdm_sor)>select name,account  from SOR_EVT_TB_REPAY_PLAN_HISlimit 3;

優化四:開啟並行執行

       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

當然,得是在系統資源比較空閒的時候才有優勢,否則,沒資源,並行也起不來。

優化五:行列過濾,程式碼優化

列處理:在SELECT中,只拿需要的列,如果有,儘量使用分割槽過濾,少用SELECT *

行處理:在分割槽剪裁中,當使用外關聯時,如果將副表的過濾條件寫在Where後面,那麼就會先全表關聯,之後再過濾,這樣效率低,可以直接子查詢後再關聯。(這個非常有用,實際開發中大資料處理效率會高很多,善用子查詢關聯)

案例實操:

(1)測試先關聯兩張表,再用where條件過濾

    hive (default)> select o.id from bigtable b

                             join ori o on o.id = b.id

                           where o.id <= 10;

    Timetaken: 34.406 seconds, Fetched: 100 row(s)

(2)通過子查詢後,再關聯表

   hive (default)> select b.id from bigtable b

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

    Timetaken: 30.058 seconds, Fetched: 100 row(s)

   但是這種對左外關聯和右外關聯有時候就不一定適用了。尤其表中有null值的時候就不適合使用了,使用時要注意。

下面將where的子句分別寫到where中和寫到子查詢裡的區別。
select  b.due_bill_no
from  
(
select distinct due_bill_no  from FDM_DM.DM_PLSADM_FEEDBACK_LOAN_RECOVERY_INFO_M where statis_date = '20180228'
) b  
left  join 
(select distinct loan_no from FDM_DM.tmp_dmp_plsadm_tradeinfo_m_report where statis_date ='20180228') a
on   b.due_bill_no  =a.loan_no 
where  a.loan_no  is  null
上面將條件寫到where子句,查詢的是在b表中存在的due_bill_no,且其在a表中不存在。
----------------------------------------------------------------------
select  b.due_bill_no
from  
(
select distinct due_bill_no  from FDM_DM.DM_PLSADM_FEEDBACK_LOAN_RECOVERY_INFO_M where statis_date = '20180228'
) b  
left  join 
(select distinct loan_no from FDM_DM.tmp_dmp_plsadm_tradeinfo_m_report where statis_date ='20180228' and  loan_no  is  null) a
on   b.due_bill_no  =a.loan_no
上面將條件寫到join的子查詢語句裡,得到的結果是b表中所有的due_bill_no結果。

優化六:開啟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>

        這個功能的缺點是,開啟JVM重用將一直佔用使用到的task插槽,以便進行重用,直到任務完成後才能釋放。如果某個“不平衡的”job中有某幾個reduce task執行的時間要比其他Reduce task消耗的時間多的多的話,那麼保留的插槽就會一直空閒著卻無法被其他的job使用,直到所有的task都結束了才會釋放。

優化七:小檔案進行合併

         在map執行前合併小檔案,減少map數:CombineHiveInputFormat具有對小檔案進行合併的功能(系統預設的格式)。HiveInputFormat沒有對小檔案合併功能。

        set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

相關文章