12.25 Hive使用場景分析,及實踐優化

概述

Hive學習也有一段時間了,今天來對Hive進行一個總結,結合自己在工作中遇到的問題,做一些具體的案例分析,當然其中有自己考慮不周的地方,或有不對的地方請大家指出相互學習,共同進步。今天來談一談什麼是Hive,產生背景,優勢等一系列問題。

什麼是Hive

Hive是一個構建在Hadoop之上的數據倉庫軟件,它可以使已經存儲的數據結構化,它提供類似sql的查詢語句HiveQL對數據進行分析處理。 Hive將HiveQL語句轉換成一系列成MapReduce作業並執行(SQL轉化為MapReduce的過程你知道嗎?)。用戶可以很方便的使用命令行和JDBC程序的方式來連接到hive。 目前,Hive除了支持MapReduce計算引擎,還支持Spark和Tez這兩中分佈式計算引擎。常用於離線批處理。 (Hive On Spark 還是試驗版本)

Hive使用場景分析,及實踐優化

Hive的產生背景

大數據的時代,海量的數據對於傳統的關係型數據庫來說維護起來成本非常高,那該如何是好,Hadoop分佈式的框架,可以使用廉價的機器部署分佈式系統把數據存儲在HDFS之上,通過MR進行計算,分析,這樣是可以的,但是,MR大家應該知道,MapReduce編程帶來的不便性,編程十分繁瑣,在大多情況下,每個MapReduce程序需要包含Mapper、Reduce和一個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集群動態添加更多機器擴展),性能,可擴展性,容錯性以及與其輸入格式的鬆散耦合。

Hive基本語法

該篇文章主要講解Hive底層的東西和一些優化。

  1. DDL
  2. DML
  3. 基本HQL
  4. 內置函數和基本的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;

Hive的優點

1.簡單易上手

2.擴展能力較好(指集群 HDFS或是YARN)

3.統一的元數據管理 metastore包括的了數據庫,表,字段分區等詳細信息

4.由於統一的元數據管理所以和spark/impala等SQL引擎是通用的

通用是指,在擁有了統一的metastore之後,在Hive中創建一張表,在Spark/impala中是能用 的,反之在Spark中創建一張表,

在Hive中也能用;只需要共用元數據,就可以切換SQL引擎

涉及到了Spark sql 和Hive On Spark(實驗版本)

5.使用SQL語法,提供快速開發的能力,支持自定義函數UDF。

6.避免了去寫mapreduce,減少開發人員學習成本。

7.數據離線處理,比如日誌分析,海量數據結構化分析

SQL轉化為MapReduce的過程

瞭解了MapReduce實現SQL基本操作之後,我們來看看Hive是如何將SQL轉化為MapReduce任務的,整個編譯過程分為六個階段:

Hive使用場景分析,及實踐優化

Antlr定義SQL的語法規則,完成SQL詞法,語法解析,將SQL轉化為抽象語法樹AST Tree

遍歷AST Tree,抽象出查詢的基本組成單元QueryBlock

遍歷QueryBlock,翻譯為執行操作樹OperatorTree

邏輯層優化器進行OperatorTree變換,合併不必要的ReduceSinkOperator,減少shuffle數據量

遍歷OperatorTree,翻譯為MapReduce任務

物理層優化器進行MapReduce任務的變換,生成最終的執行計劃

Hive內部表和外部表的區別

未被external修飾的是內部表(managed table),被external修飾的為外部表(external table);

區別

內部表數據由Hive自身管理,外部表數據由HDFS管理;

內部表數據存儲的位置是hive.metastore.warehouse.dir(默認:/user/hive/warehouse),外部表數據的存儲位置由自己制定;

刪除內部表會直接刪除元數據(metadata)及存儲數據;刪除外部表僅僅會刪除元數據,HDFS上的文件並不會被刪除;

行式存儲vs列式存儲

行式數據庫存儲在hdfs上式按行進行存儲的,一個block存儲一或多行數據。而列式數據庫在hdfs上則是按照列進行存儲,一個block可能有一列或多列數據。

如果要將數據進行壓縮:

  1. 對於行式數據庫,必然按行壓縮,當一行中有多個字段,各個字段對應的數據類型可能不一致,壓縮性能壓縮比就比較差。
  2. 對於列式數據庫,必然按列壓縮,每一列對應的是相同數據類型的數據,故列式數據庫的壓縮性能要強於行式數據庫。

如果要進行數據的查詢:

假設執行的查詢操作是: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


開始加載

insert into table emp_dynamic_partition partition(deptno)

select empno , ename , job , mgr , hiredate , sal , comm, deptno from emp;

加載數據方式並沒有指定具體的分區,只是指出了分區字段。

在select最後一個字段必須跟你的分區字段,這樣就會自行根據deptno的value來分區。

刪除分區

ALTER TABLE my_partition_test_table DROP IF EXISTS PARTITION (day='2019-10-06');

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這個參數是沒有效果的。

舉例

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個塊。

  • 增大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任務處理合適的數據量;

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. 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 = '2018-07-04' group by pt;

/group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2018-07-04 總大小為9G多,

因此這句有10個reduce

2. 調整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 = '2018-07-04' group by pt; 這次有20個reduce

3. 調整reduce個數方法二:

set mapred.reduce.tasks = 15;

select pt,count(1) from popt_tbaccountcopy_mes where pt = '2018-07-04' group by pt;這次有15個reduce

4. reduce個數並不是越多越好;

同map一樣,啟動和初始化reduce也會消耗時間和資源; 另外,有多少個reduce,就會有多少個輸出文件,如果生成了很多個小文件,那麼如果這些小文件作為下一個任務的輸入, 則也會出現小文件過多的問題;

5. 什麼情況下只有一個reduce;

很多時候你會發現任務中不管數據量多大,不管你有沒有設置調整reduce個數的參數,任務中一直都只有一個reduce任務;

其實只有一個reduce任務的情況,除了數據量小於hive.exec.reducers.bytes.per.reducer參數值的情況外,還有以下原因:

沒有group by的彙總,比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = ‘2018-07-04’ group by pt;

寫成 select count(1) from popt_tbaccountcopy_mes where pt = ‘2018-07-04’;

這點非常常見,希望大家儘量改寫。

用了Order by

有笛卡爾積

通常這些情況下,除了找辦法來變通和避免,我暫時沒有什麼好的辦法,因為這些操作都是全局的,所以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)

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格式通常會顯著減少查詢時間:

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;

5.壓縮格式:大數據場景下存儲格式壓縮格式尤為關鍵,可以提升計算速度,減少存儲空間,降低網絡io,磁盤io,所以要選擇合適的壓縮格式和存儲格式,那麼首先就瞭解這些東西。

6.MAP JOIN

Hive使用場景分析,及實踐優化

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還處於試驗階段,慎用。。

8.Use Vectorization

向量化查詢執行通過一次性批量執行1024行而不是每次單行執行,從而提高掃描,聚合,篩選器和連接等操作的性能。

在Hive 0.13中引入,此功能顯著提高了查詢執行時間,並可通過兩個參數設置輕鬆啟用:

設置hive.vectorized.execution.enabled = true;

設置hive.vectorized.execution.reduce.enabled = true;

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;

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


專注於技術熱點大數據,人工智能,JAVA、Python、 C 、GO、Javascript等語言最新前言技術,及業務痛點問題分析,請關注【編程我最懂】共同交流學習。


分享到:


相關文章: