Hive 企業級性能優化

Hive 性能問題排查方式

當我們發現一條 SQL 語句執行時間過長或者不合理時,我們就要考慮對 SQL 進行優化,優化首先得進行問題排查,那麼我們可以通過哪些方式進行排查呢。

經常使用關係型數據庫的同學可能知道關係型數據庫的優化的訣竅 - 看執行計劃。如 Oracle 數據庫,它有多種類型的執行計劃,通過多種執行計劃的配合使用,可以看到根據統計信息推演的執行計劃,即 Oracle 推斷出來的未真正運行的執行計劃;能夠觀察到從數據讀取到最終呈現的主要過程和中間的量化數據。可以說,在 Oracle 開發領域,掌握合適的環節,選用不同的執行計劃,SQL 調優就不是一件難事。

Hive 中也有執行計劃,但是 Hive 的執行計劃都是預測的,這點不像 Oracle 和 SQL Server 有真實的計劃,可以看到每個階段的處理數據、消耗的資源和處理的時間等量化數據。Hive 提供的執行計劃沒有這些數據,這意味着雖然 Hive 的使用者知道整個 SQL 的執行邏輯,但是各階段耗用的資源狀況和整個 SQL 的執行瓶頸在哪裏是不清楚的。

想要知道 HiveSQL 所有階段的運行信息,可以查看 YARN 提供的日誌。查看日誌的鏈接,可以在每個作業執行後,在控制檯打印的信息中找到。如下圖所示:

Hive 提供的執行計劃目前可以查看的信息有以下幾種

  1. 查看執行計劃的基本信息,即 explain;

  2. 查看執行計劃的擴展信息,即 explain extended;

  3. 查看 SQL 數據輸入依賴的信息,即 explain dependency;

  4. 查看 SQL 操作相關權限的信息,即 explain authorization;

  5. 查看 SQL 的向量化描述信息,即 explain vectorization。

在查詢語句的 SQL 前面加上關鍵字 explain 是查看執行計劃的基本方法。用 explain 打開的執行計劃包含以下兩部分:

Hive 中的 explain 執行計劃詳解可看我之前寫的這篇文章Hive 底層原理:explain 執行計劃詳解

注:使用 explain 查看執行計劃是 Hive 性能調優中非常重要的一種方式,請務必掌握!

總結:Hive 對 SQL 語句性能問題排查的方式

  1. 使用 explain 查看執行計劃;

  2. 查看 YARN 提供的日誌。

Hive 性能調優的方式

爲什麼都說性能優化這項工作是比較難的,因爲一項技術的優化,必然是一項綜合性的工作,它是多門技術的結合。我們如果只侷限於一種技術,那麼肯定做不好優化的。

下面將從多個完全不同的角度來介紹 Hive 優化的多樣性,我們先來一起感受下。

1. SQL 語句優化

SQL 語句優化涉及到的內容太多,因篇幅有限,不能一一介紹到,所以就拿幾個典型舉例,讓大家學到這種思想,以後遇到類似調優問題可以往這幾個方面多思考下。

1. union all

insert into table stu partition(tp) 
select s_age,max(s_birth) stat,'max' tp 
from stu_ori
group by s_age

union all

insert into table stu partition(tp) 
select s_age,min(s_birth) stat,'min' tp 
from stu_ori
group by s_age;

我們簡單分析上面的 SQL 語句,就是將每個年齡段的最大和最小的生日獲取出來放到同一張表中,union all 前後的兩個語句都是對同一張表按照 s_age 進行分組,然後分別取最大值和最小值。

上面的 SQL 對同一張表的相同字段進行兩次分組,這顯然造成了極大浪費,我們能不能改造下呢,當然是可以的,爲大家介紹一個語法: from ... insert into ... ,這個語法將 from 前置,作用就是使用一張表,可以進行多次插入操作:

--開啓動態分區 
set hive.exec.dynamic.partition=true; 
set hive.exec.dynamic.partition.mode=nonstrict; 

from stu_ori 

insert into table stu partition(tp) 
select s_age,max(s_birth) stat,'max' tp 
group by s_age

insert into table stu partition(tp) 
select s_age,min(s_birth) stat,'min' tp 
group by s_age;

上面的 SQL 就可以對 stu_ori 表的 s_age 字段分組一次而進行兩次不同的插入操作。

這個例子告訴我們一定要多瞭解 SQL 語句,如果我們不知道這種語法,一定不會想到這種方式的

2. distinct

先看一個 SQL,去重計數:

select count(1) 
from( 
  select s_age 
  from stu 
  group by s_age 
) b;

這是簡單統計年齡的枚舉值個數,爲什麼不用 distinct?

select count(distinct s_age) 
from stu;

有人說因爲在數據量特別大的情況下使用第一種方式 (group by) 能夠有效避免 Reduce 端的數據傾斜,但事實如此嗎?

我們先不管數據量特別大這個問題,就當前的業務和環境下使用 distinct 一定會比上面那種子查詢的方式效率高。原因有以下幾點:

  1. 上面進行去重的字段是年齡字段,要知道年齡的枚舉值是非常有限的,就算計算 1 歲到 100 歲之間的年齡,s_age 的最大枚舉值才 100,如果轉化成 MapReduce 來解釋的話,在 Map 階段,每個 Map 會對 s_age 去重。由於 s_age 枚舉值有限,因而每個 Map 得到的 s_age 也有限,最終得到 reduce 的數據量也就是 map 數量 * s_age 枚舉值的個數。這個數量是很小的。

  2. distinct 的命令會在內存中構建一個 hashtable,查找去重的時間複雜度是 O(1);group by 在不同版本間變動比較大,有的版本會用構建 hashtable 的形式去重,有的版本會通過排序的方式, 排序最優時間複雜度無法到 O(1)。另外,第一種方式 (group by) 去重會轉化爲兩個任務,會消耗更多的磁盤網絡 I/O 資源。

  3. 最新的 Hive 3.0 中新增了 count(distinct) 優化,通過配置 hive.optimize.countdistinct,即使真的出現數據傾斜也可以自動優化,自動改變 SQL 執行的邏輯。

  4. 第二種方式 (distinct) 比第一種方式 (group by) 代碼簡潔,表達的意思簡單明瞭,如果沒有特殊的問題,代碼簡潔就是優!

這個例子告訴我們,有時候我們不要過度優化,調優講究適時調優,過早進行調優有可能做的是無用功甚至產生負效應,在調優上投入的工作成本和回報不成正比。調優需要遵循一定的原則

2. 數據格式優化

Hive 提供了多種數據存儲組織格式,不同格式對程序的運行效率也會有極大的影響。

Hive 提供的格式有 TEXT、SequenceFile、RCFile、ORC 和 Parquet 等。

SequenceFile 是一個二進制 key/value 對結構的平面文件,在早期的 Hadoop 平臺上被廣泛用於 MapReduce 輸出 / 輸出格式,以及作爲數據存儲格式。

Parquet 是一種列式數據存儲格式,可以兼容多種計算引擎,如 MapRedcue 和 Spark 等,對多層嵌套的數據結構提供了良好的性能支持,是目前 Hive 生產環境中數據存儲的主流選擇之一。

ORC 優化是對 RCFile 的一種優化,它提供了一種高效的方式來存儲 Hive 數據,同時也能夠提高 Hive 的讀取、寫入和處理數據的性能,能夠兼容多種計算引擎。事實上,在實際的生產環境中,ORC 已經成爲了 Hive 在數據存儲上的主流選擇之一。

我們執行同樣的 SQL 語句及同樣的數據,只是數據存儲格式不同,得到如下執行時長:

注:CPU 時間:表示運行程序所佔用服務器 CPU 資源的時間。
用戶等待耗時:記錄的是用戶從提交作業到返回結果期間用戶等待的所有時間。

查詢 TextFile 類型的數據表耗時 33 分鐘, 查詢 ORC 類型的表耗時 1 分 52 秒,時間得以極大縮短,可見不同的數據存儲格式也能給 HiveSQL 性能帶來極大的影響。

3. 小文件過多優化

小文件如果過多,對 hive 來說,在進行查詢時,每個小文件都會當成一個塊,啓動一個 Map 任務來完成,而一個 Map 任務啓動和初始化的時間遠遠大於邏輯處理的時間,就會造成很大的資源浪費。而且,同時可執行的 Map 數量是受限的。

所以我們有必要對小文件過多進行優化,關於小文件過多的解決的辦法,我之前專門寫了一篇文章講解,具體可查看:解決 hive 小文件過多問題

4. 並行執行優化

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。

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

5. 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 都結束了纔會釋放。

6. 推測執行優化

在分佈式集羣環境下,因爲程序 bug(包括 Hadoop 本身的 bug),負載不均衡或者資源分佈不均等原因,會造成同一個作業的多個任務之間運行速度不一致,有些任務的運行速度可能明顯慢於其他任務(比如一個作業的某個任務進度只有 50%,而其他所有任務已經運行完畢),則這些任務會拖慢作業的整體執行進度。爲了避免這種情況發生,Hadoop 採用了推測執行(Speculative Execution)機制,它根據一定的法則推測出 “拖後腿” 的任務,併爲這樣的任務啓動一個備份任務,讓該任務與原始任務同時處理同一份數據,並最終選用最先成功運行完成任務的計算結果作爲最終結果。

設置開啓推測執行參數: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 的推測執行:

set hive.mapred.reduce.tasks.speculative.execution=true

關於調優這些推測執行變量,還很難給一個具體的建議。如果用戶因爲輸入數據量很大而需要執行長時間的 map 或者 reduce task 的話,那麼啓動推測執行造成的浪費是非常巨大的。


最後

代碼優化原則:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/-llCkKB1zYJyYKkAqCWZwg