Hive优化语句
hive建表設計層面
1.使用分區表優化
分區表 是在某一個或者幾個維度上對數據進行分類存儲,一個分區對應一個目錄。如果篩選條件里有分
區字段,那么 Hive 只需要遍歷對應分區目錄下的文件即可,不需要遍歷全局數據,使得處理的數據量
大大減少,從而提高查詢效率。
也就是說:當一個 Hive 表的查詢大多數情況下,會根據某一個字段進行篩選時,那么非常適合創建為
分區表,該字段即為分區字段。
2.使用分桶表優化
跟分區的概念很相似,都是把數據分成多個不同的類別,區別就是規則不一樣!
1、分區:按照字段值來進行:一個分區,就只是包含這個這一個值的所有記錄
不是當前分區的數據一定不在當前分區
當前分區也只會包含當前這個分區值的數據
2、分桶:默認規則:Hash散列
一個分桶中會有多個不同的值
如果一個分桶中,包含了某個值,這個值的所有記錄,必然都在這個分桶
Hive Bucket,分桶,是指將數據以指定列的值為 key 進行 hash,hash 到指定數目的桶中,這樣做的
目的和分區表類似,使得篩選時不用全局遍歷所有的數據,只需要遍歷所在桶就可以了。這樣也可以支
持高效采樣。
3.選擇合適的文件存儲格式
Apache Hive支持 Apache Hadoop 中使用的幾種熟悉的文件格式,比如 TextFile、SequenceFile、RCFile、Avro、ORC、ParquetFile等。
存儲格式一般需要根據業務進行選擇,在我們的實操中,絕大多數表都采用TextFile與Parquet兩種存儲
格式之一。 TextFile是最簡單的存儲格式,它是純文本記錄,也是Hive的默認格式。雖然它的磁盤開銷
比較大,查詢效率也低,但它更多地是作為跳板來使用。RCFile、ORC、Parquet等格式的表都不能由
文件直接導入數據,必須由TextFile來做中轉。 Parquet和ORC都是Apache旗下的開源列式存儲格式。
列式存儲比起傳統的行式存儲更適合批量OLAP查詢,并且也支持更好的壓縮和編碼。
創建表時,特別是寬表,盡量使用 ORC、ParquetFile 這些列式存儲格式,因為列式存儲的表,每一
列的數據在物理上是存儲在一起的,Hive查詢時會只遍歷需要列數據,大大減少處理的數據量。
第一種:TextFile
1、存儲方式:行存儲。默認格式,如果建表時不指定默認為此格式。,
2、每一行都是一條記錄,每行都以換行符"\n"結尾。數據不做壓縮時,磁盤會開銷比較大,數據解析開銷也 比較大。
3、可結合Gzip、Bzip2等壓縮方式一起使用(系統會自動檢查,查詢時會自動解壓),推薦選用可切分的壓 縮算法。
第二種:Sequence File
1、一種Hadoop API提供的二進制文件,使用方便、可分割、個壓縮的特點。
2、支持三種壓縮選擇:NONE、RECORD、BLOCK。RECORD壓縮率低,一般建議使用BLOCK壓縮。
第三種:RC File
1、存儲方式:數據按行分塊,每塊按照列存儲 。
A、首先,將數據按行分塊,保證同一個record在一個塊上,避免讀一個記錄需要讀取多個block。
B、其次,塊數據列式存儲,有利于數據壓縮和快速的列存取。
2、相對來說,RCFile對于提升任務執行性能提升不大,但是能節省一些存儲空間。可以使用升級版的ORC格 式。
第四種:ORC File
1、存儲方式:數據按行分塊,每塊按照列存儲
2、Hive提供的新格式,屬于RCFile的升級版,性能有大幅度提升,而且數據可以壓縮存儲,壓縮快,快速 列存取。 3、ORC
File會基于列創建索引,當查詢的時候會很快。
第五種:Parquet File
1、存儲方式:列式存儲。
2、Parquet對于大型查詢的類型是高效的。對于掃描特定表格中的特定列查詢,Parquet特別有用。
Parquet一般使用Snappy、Gzip壓縮。默認Snappy。
3、Parquet支持Impala 查詢引擎。
4、表的文件存儲格式盡量采用Parquet或ORC,不僅降低存儲量,還優化了查詢,壓縮,表關聯等性能。
4.選擇合適的壓縮格式
Hive 語句最終是轉化為 MapReduce 程序來執行的,而 MapReduce 的性能瓶頸在與 網絡IO 和 磁盤
IO,要解決性能瓶頸,最主要的是 減少數據量,對數據進行壓縮是個好方式。壓縮雖然是減少了數據
量,但是壓縮過程要消耗 CPU,但是在 Hadoop 中,往往性能瓶頸不在于 CPU,CPU 壓力并不大,所
以壓縮充分利用了比較空閑的 CPU。
map階段輸出數據壓縮 ,在這個階段,優先選擇一個低CPU開銷的算法。
set hive.exec.compress.intermediate=true set mapred.map.output.compression.codec= org.apache.hadoop.io.compress.SnappyCodec set mapred.map.output.compression.codec=com.hadoop.compression.lzo.LzoCodec;對最終輸出結果壓縮
set hive.exec.compress.output=true set mapred.output.compression.codec=org.apache.hadoop.io.compress.SnappyCodec##當然,也可以在hive建表時指定表的文件格式和壓縮編碼
結論,一般選擇orcfile/parquet + snappy 方式
sql語法和運行參數層面
1.列裁剪
列裁剪就是在查詢時只讀取需要的列,分區裁剪就是只讀取需要的分區。當列很多或者數據量很大時,
如果 select * 或者不指定分區,全列掃描和全表掃描效率都很低。
Hive 在讀數據的時候,可以只讀取查詢中所需要用到的列,而忽略其他的列。這樣做可以節省讀取開
銷:中間表存儲開銷和數據整合開銷。
2.謂詞下推
將 SQL 語句中的 where 謂詞邏輯都盡可能提前執行,減少下游處理的數據量。對應邏輯優化器是
PredicatePushDown。
3.合并小文件
在執行 MapReduce 程序的時候,一般情況是一個文件的一個數據分塊需要一個 mapTask 來處理。但
是如果數據源是大量的小文件,這樣就會啟動大量的 mapTask 任務,這樣會浪費大量資源。可以將輸
入的小文件進行合并,從而減少 mapTask 任務數量。
4.合理控制map task和reduce task的數量
這個優化措施,但凡能用就用! 大表 join 小表 小表滿足需求: 小表數據小于控制條件時
MapJoin 是將 join 雙方比較小的表直接分發到各個 map 進程的內存中,在 map 進程中進行 join 操
作,這樣就不用進行 reduce 步驟,從而提高了速度。只有 join 操作才能啟用 MapJoin。
5.join 數據傾斜優化
在編寫 Join 查詢語句時,如果確定是由于 join 出現的數據傾斜,那么請做如下設置:
join的鍵對應的記錄條數超過這個值則會進行分拆,值根據具體數據量設置
set hive.skewjoin.key=100000;如果是join過程出現傾斜應該設置為true
set hive.optimize.skewjoin=false;如果開啟了,在 Join 過程中 Hive 會將計數超過閾值 hive.skewjoin.key(默認100000)的傾斜 key 對
應的行臨時寫進文件中,然后再啟動另一個 job 做 map join 生成結果。
通過 hive.skewjoin.mapjoin.map.tasks 參數還可以控制第二個 job 的 mapper 數量,默認
10000。
6.CBO優化
join的時候表的順序的關系:前面的表都會被加載到內存中。后面的表進行磁盤掃描。
select a.*, b.*, c.* from a join b on a.id = b.id join c on a.id = c.id;Hive 自 0.14.0 開始,加入了一項 “Cost based Optimizer” 來對 HQL 執行計劃進行優化,這個功能通
過 “hive.cbo.enable” 來開啟。在 Hive 1.1.0 之后,這個 feature 是默認開啟的,它可以 自動優化 HQL
中多個 Join 的順序,并選擇合適的 Join 算法。
CBO,成本優化器,代價最小的執行計劃就是最好的執行計劃。傳統的數據庫,成本優化器做出最優化
的執行計劃是依據統計信息來計算的。Hive 的成本優化器也一樣。
Hive 在提供最終執行前,優化每個查詢的執行邏輯和物理執行計劃。這些優化工作是交給底層來完成
的。根據查詢成本執行進一步的優化,從而產生潛在的不同決策:如何排序連接,執行哪種類型的連
接,并行度等等。
要使用基于成本的優化(也稱為CBO),請在查詢開始設置以下參數:
hive架構層面
1.啟用本地抓取
Hive 的某些 SQL 語句需要轉換成 MapReduce 的操作,某些 SQL 語句就不需要轉換成 MapReduce 操
作,但是同學們需要注意,理論上來說,所有的 SQL 語句都需要轉換成 MapReduce 操作,只不過
Hive 在轉換 SQL 語句的過程中會做部分優化,使某些簡單的操作不再需要轉換成 MapReduce,例
如:
1、只是 select * 的時候
2、where 條件針對分區字段進行篩選過濾時
3、帶有 limit 分支語句時
Hive 從 HDFS 中讀取數據,有兩種方式:啟用MapReduce讀取 和 直接抓取。
直接抓取數據比 MapReduce 方式讀取數據要快的多,但是只有少數操作可以使用直接抓取方式。
可以通過 hive.fetch.task.conversion 參數來配置在什么情況下采用直接抓取方式:
minimal:只有 select * 、在分區字段上 where 過濾、有 limit 這三種場景下才啟用直接抓取方式。 more:在
select、where 篩選、limit 時,都啟用直接抓取方式。
2.本地執行優化
Hive 在集群上查詢時,默認是在集群上多臺機器上運行,需要多個機器進行協調運行,這種方式很好
的解決了大數據量的查詢問題。但是在 Hive 查詢處理的數據量比較小的時候,其實沒有必要啟動分布
式模式去執行,因為以分布式方式執行設計到跨網絡傳輸、多節點協調等,并且消耗資源。對于小數據
集,可以通過本地模式,在單臺機器上處理所有任務,執行時間明顯被縮短。
啟動本地模式涉及到三個參數:
--打開hive自動判斷是否啟動本地模式的開關 set hive.exec.mode.local.auto=true; -- map任務數最大值,不啟用本地模式的task最大個數 set hive.exec.mode.local.auto.input.files.max=4; -- map輸入文件最大大小,不啟動本地模式的最大輸入文件大小 set hive.exec.mode.local.auto.inputbytes.max=134217728;3.JVM重用
Hive 語句最終會轉換為一系列的 MapReduce 任務,每一個MapReduce 任務是由一系列的 MapTask
和 ReduceTask 組成的,默認情況下,MapReduce 中一個 MapTask 或者 ReduceTask 就會啟動一個
JVM 進程,一個 Task 執行完畢后,JVM 進程就會退出。這樣如果任務花費時間很短,又要多次啟動
JVM 的情況下,JVM 的啟動時間會變成一個比較大的消耗,這時,可以通過重用 JVM 來解決。
JVM也是有缺點的,開啟JVM重用會一直占用使用到的 task 的插槽,以便進行重用,直到任務完成后才
會釋放。如果某個 不平衡的job 中有幾個 reduce task 執行的時間要比其他的 reduce task 消耗的時間
要多得多的話,那么保留的插槽就會一直空閑卻無法被其他的 job 使用,直到所有的 task 都結束了才
會釋放。
根據經驗,一般來說可以使用一個 cpu core 啟動一個 JVM,假如服務器有 16 個 cpu core ,但是這個
節點,可能會啟動 32 個mapTask,完全可以考慮:啟動一個JVM,執行兩個Task。
4.并行執行
有的查詢語句,Hive 會將其轉化為一個或多個階段,包括:MapReduce 階段、抽樣階段、合并階段、
limit 階段等。默認情況下,一次只執行一個階段。但是,如果某些階段不是互相依賴,是可以并行執行
的。多階段并行是比較耗系統資源的。
一個 Hive SQL 語句可能會轉為多個 MapReduce Job,每一個 job 就是一個 stage,這些 Job 順序執
行,這個在 cli 的運行日志中也可以看到。但是有時候這些任務之間并不是是相互依賴的,如果集群資
源允許的話,可以讓多個并不相互依賴 stage 并發執行,這樣就節約了時間,提高了執行速度,但是如
果集群資源匱乏時,啟用并行化反倒是會導致各個 Job 相互搶占資源而導致整體執行性能的下降。啟用
并行化:
sql優化
1 對于這個調優,有待再次驗證目前我驗證了4千萬 join 8千萬發現不管是left join還是inner join,執行計劃差不多,job數,maptask,reducetask數都一樣,執行時長也差不多可能都已經自動優化了(謂詞下推)
select count(1) from test1 t1 inner join test2 t2 on t1.uuid = t2.uuid and t2.date_id = ‘2020-10-23’ where t1.date_id = ‘2020-10-23’;select count(1) from (select * from test1 where date_id = ‘2020-10-23’) t1 inner join test2 t2 on t1.uuid = t2.uuid and t2.date_id = ‘2020-10-23’;
2 .union優化
盡量不要使用union (union 去掉重復的記錄)而是使用 union all 然后再用group by 去重
3 .count distinct優化
數據量小的時候無所謂,數據量大的情況下,由于COUNT DISTINCT操作需要用一個Reduce Task來完成,這一個Reduce需要處理的數據量太大,就會導致整個Job很難完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替換:
不要使用count (distinct cloumn) ,使用子查詢
雖然會多用一個Job來完成,但在數據量大的情況下,這個絕對是值得的。
4.用in 來代替join
如果需要根據一個表的字段來約束另為一個表,盡量用in來代替join . in 要比join 快
select a.Id ,name from tb1 a join tb2 b on(a.id = b.id); select Id ,name from tb1 where id in(select id from tb2);5. Left semi join優化in/exists
(a表和b表通過user_id關聯)
a表數據
b表數據
l
如圖所示:只能展示a表的字段,因為left semi join 只傳遞表的 join key 給 map 階段
總結
- 上一篇: Windows下 C++ API函数大全
- 下一篇: 大数据周周看:百分点集团全资并购极速洞察