1. 程式人生 > >HIVE基礎知識及優化(面試必備)

HIVE基礎知識及優化(面試必備)

hive是基於Hadoop的一個數據倉庫工具,可以將結構化的資料檔案對映為一張資料庫表,並提供簡單的sql查詢功能,可以將sql語句轉換為MapReduce任務進行執行。

Metastore (hive元資料)
Hive將元資料儲存在資料庫中,比如mysql ,derby.Hive中的元資料包括表的名稱,表的列和分割槽及其屬性,表的資料所在的目錄

Hive資料儲存在HDFS,大部分的查詢、計算由mapreduce完成

Hive資料倉庫於資料庫的異同

 (1)由於Hive採用了SQL的查詢語言HQL,因此很容易將Hive理解為資料庫。其實從結構上來看,Hive和資料庫除了擁有類似的查詢語言,
 再無類似之處。
(2)資料儲存位置。  hdfs   raw local fs
(3)資料格式。 分隔符
(4)資料更新。hive讀多寫少。Hive中不支援對資料的改寫和新增,所有的資料都是在載入的時候中確定好的。
INSERT INTO … VALUES新增資料,使用UPDATE … SET修改資料  不支援的
HDFS 一次寫入多次讀取
(5) 執行。hive通過MapReduce來實現的  而資料庫通常有自己的執行引擎。 
(6)執行延遲。由於沒有索引,需要掃描整個表,因此延遲較高。另外一個導致Hive執行延遲高的因素是MapReduce框架
(7)可擴充套件性
(8)資料規模。

hive幾種基本表型別:內部表、外部表、分割槽表、桶表

內部表(管理表)和外部表的區別:

建立表
外部表建立表的時候,不會移動數到資料倉庫目錄中(/user/hive/warehouse),只會記錄表資料存放的路徑
內部表會把資料複製或剪下到表的目錄下
刪除表
外部表在刪除表的時候只會刪除表的元資料資訊不會刪除表資料
內部表刪除時會將元資料資訊和表資料同時刪除

表型別一、管理表或內部表Table Type: MANAGED_TABLE

create table if not exists dept(
deptno int,
deptname string,
address string
)
row
format delimited fields terminated by '\t';
//載入HDFS檔案到Hive表中 load data inpath '/input/dept.txt' into table dept; //用來指定原檔案的列分隔符 row format delimited fields terminated by '\t'; load 如果操作的HDFS上的檔案,代表著會移動或者剪下檔案 desc formatted dept; //描述表結構資訊 Location: hdfs://bigdata/user/hive/warehouse/db01.db/dept Table Type: MANAGED_TABLE

表型別二、外部表

create  external table emp(
empno int,
empname string,
empjob string,
mgno int,
birthday string,
salary float,
bonus float,
depno int
)
row  format delimited fields terminated by '\t'
location '/input/demo';

//描述表結構
desc formatted emp;
Location:               hdfs://bigdata/input/demo    
Table Type:             EXTERNAL_TABLE 

刪除內部表
drop table dept;

刪除外部表
drop table emp;

清空表資料
truncate table student;

表型別三、分割槽表

分割槽表建立表的時候需要指定分割槽欄位,分割槽欄位與普通欄位的區別:分割槽欄位會在HDFS表目錄下生成一個分割槽欄位名稱的目錄,而普通欄位則不會,查詢的時候可以當成普通欄位來使用,一般不直接和業務直接相關。

 create  table emp_part(
 empno int,
 empname string,
 empjob string,
 mgrno int,
 birthday string,
 salary float,
 bonus float,
 deptno   int
 )
 partitioned by (province string)
row format delimited fields terminated by '\t';

//向分割槽表載入資料
load data local inpath '/home/user01/emp.txt' into table emp_part partition (province='CHICAGO');

//描述表資訊
desc formatted  emp_part;

//查詢全表資料
select * from emp_part;

//查詢分割槽欄位表資料
select * from emp_part where province='CHICAGO';

//檢視分割槽資訊
show partitions emp_part;

//增加分割槽
aler table emp_part add [if not exist] partition(provine='zhejiang',city='hangzhou') 

//刪除分割槽
aler table emp_part drop [if  exist] partition(provine='zhejiang',city='hangzhou')

外部分割槽表

create external  table  dept_part(
deptno int,
deptname string,
address string
)
partitioned by (province string)
row  format delimited fields terminated by '\t'
location '/input/demo';

//手動增加分割槽欄位及外部目錄:
alter table dept_part add partition (province='BOSTON') location '/input/demo/BOSTON';

//手動增加分割槽欄位(自動生成分割槽目錄)
alter table dept_part add partition (province='NEW YORK');

表型別四:桶表

將內部表,外部表和分割槽表進一步組織成桶表
可以將表的列通過Hash演算法進一步分解成不同的檔案儲存

create table test_bucket_table(
id int,
name string
)
clustered by (id) into 5 bucket;

建立表的方式

方式一 create + load

    create [external] table table_name(
    col1_name col1_type,
    ...
    coln_name coln_type
    )
    row  format delimited fields terminated  by '\t';

   //load載入資料
    laod data [local] inpth '本地檔案(linux)/HDFS' [overwrite] into table  table_name;

方式二 like + load

    //複製表結構
    create table tableB like tableA;    //首先必須要有tableA

    //load載入資料
    laod data [local] inpth '本地檔案(linux)/HDFS' [overwrite] into table  table_name;

方式三 as 建立表的同時載入資料

create table tableB row format delimited filelds termianted by ','  as select * from tableA;   //首先必須要有tableA

 create table emp_as row format delimited fields terminated by ',' as select empno,empname,salary from emp_part1;

方式四 create + insert

//建立表
create table emp_insert(
id int,
name string,
job string,
salary float
)
row format  delimited fields terminated by ',';

//insert into 載入資料
insert into table emp_insert select empno,empname,empjob,salary from emp_part1 where day='20170308' and  hour='14';

載入資料的方式

載入方式一

//載入本地檔案到Hive表   --使用儲存介質(行動硬碟)
laod data local inpth '本地檔案(linux)' [overwrite] into table  table_name;

載入方式二

//載入HDFS檔案到hive表   --通過Flume等日誌收集框架
laod data  inpth 'HDFS檔案' [overwrite] into table  table_name;

載入方式三

//載入資料示覆蓋已有的資料
laod data [local] inpth '檔案' overwrite  into table;

//**hdfs  dfs -put 本地目錄及檔案 表在HDFS所在的目錄
 desc formatted table_name;  --找到table_name在HDFS上的目錄

載入方式四

 //建立表時通過select查詢語句載入資料
 create table tableB row format delimited filelds termianted by ','  as select * from tableA;  

載入方式五

//先建立表,通過insert into table table_namea  select  *  fom tableB

載入方式六

//建立外部表示通過location指定資料所在目錄
create extrnal table_name(
     col1_name col1_type,
     ...
     coln_name coln_type
)
row  format delimited fields terminated  by '\t';
location  'HDFS上的目錄'

幾種匯出資料的方式

    1.insert overwrite ... 匯出到本地目錄
     insert overwrite local directory '/home/user01/export' row format delimited fields terminated by ' ' select * from emp_part1;

    2.insert overwrite ... 匯出到HDFS之上
    insert overwrite directory '/export' select * from emp_part1 where day='20170308';

    3.hive -e 'HQL query'  >> test
    bin/hive -e 'select * from db01.student' >> test.txt

    4)sqoop

Hive 自定義函式函式

UDF 一進一出 處理原檔案內容某些欄位包含 [] “”
UDAF 多進一出 sum() avg() max() min()
UDTF 一進多出 ip -> 國家 省 市

Hive4種排序

  1. order by //可以指定desc 降序 asc 升序
    order by會對輸入做全域性排序,因此只有一個Reducer(多個Reducer無法保證全域性有序),然而只有一個Reducer,會導致當輸入規模較大時,消耗較長的計算時間。

  2. sort by 【對分割槽內的資料進行排序】
    sort by不是全域性排序,其在資料進入reducer前完成排序,因此,如果用sort by進行排序,並且設定mapred.reduce.tasks>1,則sort by只會保證每個reducer的輸出有序,並不保證全域性有序。sort by不同於order by,它不受Hive.mapred.mode屬性的影響,sort by的資料只能保證在同一個reduce中的資料可以按指定欄位排序。使用sort by你可以指定執行的reduce個數(通過set mapred.reduce.tasks=n來指定),對輸出的資料再執行歸併排序,即可得到全部結果。

  3. distribute by 【對map輸出進行分割槽】
    distribute by是控制在map端如何拆分資料給reduce端的。hive會根據distribute by後面列,對應reduce的個數進行分發,預設是採用hash演算法。sort by為每個reduce產生一個排序檔案。在有些情況下,你需要控制某個特定行應該到哪個reducer,這通常是為了進行後續的聚集操作。distribute by剛好可以做這件事。因此,distribute by經常和sort by配合使用。

  4. cluster by
    cluster by除了具有distribute by的功能外還兼具sort by的功能。當distribute by和sort by 是同一個欄位的時候可以使用cluster by替代。但是排序只能是倒敘排序,不能指定排序規則為ASC或者DESC。

三種分組的區別

  1. row_number:不管col2欄位的值是否相等,行號一直遞增,比如:有兩條記錄的值相等,但一個是第一,一個是第二
  2. rank:上下兩條記錄的col2相等時,記錄的行號是一樣的,但下一個col2值的行號遞增N(N是重複的次數),比如:有兩條並列第一,下一個是第三,沒有第二
  3. dense_rank:上下兩條記錄的col2相等時,下一個col2值的行號遞增1,比如:有兩條並列第一,下一個是第二

Hive優化

  • 1.fetch task任務不走MapReduce,可以在hive配置檔案中設定最大化和最小化fetch task任務;通常在使用hiveserver2時調整為more;
設定引數的優先順序:在命令列或者程式碼設定引數 > hive-site.xml>hive-default.xml
 set hive.fetch.task.conversion=more;   //單次互動模式下有效,
 bin/hive --hiveconf hive.fetch.task.conversion=more

上面的兩種方法都可以開啟了Fetch任務,但是都是臨時起作用的;如果你想一直啟用這個功能,可以在${HIVE_HOME}/conf/hive-site.xml裡面加入以下配置:
<property>
  <name>hive.fetch.task.conversion</name>
  <value>more</value>
  <description>
    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 incurrs RS), 
    lateral views and joins.
    1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only
    2. more    : SELECT, FILTER, LIMIT only (+TABLESAMPLE, virtual columns)
  </description>
</property>
  • 2.strict mode:嚴格模式設定,嚴格模式下將會限制一些查詢操作
    檔案格式,ORC PARQUET 等
    分割槽表
    select 查詢不加where過濾條件,不會執行
開啟嚴格模式
hive提供的嚴格模式,禁止3種情況下的查詢模式。
a:當表為分割槽表時,where字句後沒有分割槽欄位和限制時,不允許執行。
b:當使用order by語句時,必須使用limit欄位,因為order by 只會產生一個reduce任務。
c:限制笛卡爾積的查詢。sql語句不加where不會執行

<property>
  <name>hive.mapred.mode</name>
  <value>nonstrict</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>
  • 3.優化sql語句,如先過濾再join,先分組再做distinct;
Select count(*) cnt
From store_sales ss
     join household_demographics hd on (ss.ss_hdemo_sk = hd.hd_demo_sk)
     join time_dim t on (ss.ss_sold_time_sk = t.t_time_sk)
     join store s on (s.s_store_sk = ss.ss_store_sk)
Where
     t.t_hour = 8
     t.t_minute >= 30
     hd.hd_dep_count = 2
order by cnt;
  • 4.MapReduce過程的map、shuffle、reduce端的snappy壓縮

    需要先替換hadoop的native本地包開啟壓縮
    在mapred-site.xml檔案設定啟用壓縮及壓縮編碼
    在執行SQL執行時設定啟用壓縮和指定壓縮編碼

set mapreduce.output.fileoutputformat.compress=true;
set mapreduce.output.fileoutputformat.compress.codec=org apache.hadoop.io.compress.SnappyCodec;
  • 5.大表拆分成子表,提取中間結果集,減少每次載入資料
    多維度分析,多個分析模組
    每個分析模組涉及欄位不一樣,而且並不是表的全部欄位

  • 6.分割槽表及外部表
    設計二級分割槽表(一級欄位為天,二級欄位設定小時)
    建立的的是外部表,建立表時直接指定資料所在目錄即可,不用再用load載入資料

  • 7.設定map和reduce個數:預設情況下一個塊對應一個map任務,map資料我們一般不去調整,reduce個數根據reduce處理的資料量大小進行適當調整體現“分而治之”的思想

    set mapred.reduce.tasks=3;
  • 8.JVM重用:一個job可能有多個map reduce任務,每個任務會開啟一個JVM虛擬機器,預設情況下一個任務對應一個JVM,任務執行完JVM即銷燬,我們可以設定JVM重用引數,一般不超過5個,這樣一個JVM內可以連續執行多個任務

JVM重用是Hadoop調優引數的內容,對Hive的效能具有非常大的影響,特別是對於很難避免小檔案的場景或者task特別多的場景,這類場景大多數執行時間都很短。hadoop預設配置是使用派生JVM來執行map和reduce任務的,這是jvm的啟動過程可能會造成相當大的開銷,尤其是執行的job包含有成千上萬個task任務的情況。
JVM重用可以使得JVM例項在同一個JOB中重新使用N次,N的值可以在Hadoop的mapre-site.xml檔案中進行設定(建議參考5~10)
mapred.job.reuse.jvm.num.tasks(舊版)
mapreduce.job.jvm.numtasks(新版)
hadoop.apache.org/docs/r2.5.2/hadoop-mapreduce-client/hadoop-mapreduce-client-core/mapred-default.xml
http://hadoop.apache.org/docs/r2.5.2/hadoop-mapreduce-client/hadoop-mapreduce-client-core/mapred-default.xml

也可在hive的執行設定:

***set mapred.job.reuse.jvm.num.tasks=10;
hive (default)> set mapred.job.reuse.jvm.num.tasks;
mapred.job.reuse.jvm.num.tasks=1
  • 9.推測執行:例如一個Job應用有10個MapReduce任務(map 及reduce),其中9個任務已經完成,那麼application Master會在另外啟動一個相同的任務來執行未完成的那個,最後哪個先執行完成就把另一個kill掉

    啟用speculative最大的好處是,一個map執行的時候,系統會在其他空閒的伺服器上啟動相同的map來同時執行,哪個執行的快就使用哪個的結果,另一個執行慢的在有了結果之後就會被kill。

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

資料傾斜

對於普通的join操作,會在map端根據key的hash值,shuffle到某一個reduce上去,在reduce端做join連線操作,記憶體中快取join左邊的表,遍歷右邊的表,依次做join操作。所以在做join操作時候,將資料量多的表放在join的右邊。

當資料量比較大,並且key分佈不均勻,大量的key都shuffle到一個reduce上了,就出現了資料的傾斜。

常見的資料傾斜出現在group by和join..on..語句中。

join(資料傾斜)
在進行兩個表join的過程中,由於hive都是從左向右執行,要注意講小表在前,大表在後(小表會先進行快取)。

map/reduce程式執行時,reduce節點大部分執行完畢,但是有一個或者幾個reduce節點執行很慢,導致整個程式的處理時間很長,這是因為某一個key的條數比其他key多很多(有時是百倍或者千倍之多),這條key所在的reduce節點所處理的資料量比其他節點就大很多,從而導致某幾個節點遲遲執行不完,此稱之為資料傾斜。hive在跑資料時經常會出現資料傾斜的情況,使的作業經常reduce完成在99%後一直卡住,最後的1%花了幾個小時都沒跑完,這種情況就很可能是資料傾斜的原因,

hive.groupby.skewindata=true; 
如果是group by過程出現傾斜應將此項設定true。
<property>
  <name>hive.groupby.skewindata</name>
  <value>false</value>
  <description>Whether there is skew in data to optimize group by queries</description>
</property>
hive.optimize.skewjoin.compiletime=true;
如果是join 過程中出現傾斜應將此項設定為true
不影響結果可以考慮過濾空值
<property>
  <name>hive.optimize.skewjoin.compiletime</name>
  <value>false</value>
</property>  

hive.optimize.skewjoin.compiletime=true; 如果是join過程出現傾斜應該設定為true
此時會將join語句轉化為兩個mapreduce任務,第一個會給jion欄位加隨機雜湊
set hive.skewjoin.key=100000; 這個是join的鍵對應的記錄條數超過這個值則會進行優化。

可以在空值前面加隨機雜湊

3種常見的join

Map-side Join

mapJoin的主要意思就是,當連結的兩個表是一個比較小的表和一個特別大的表的時候,我們把比較小的table直接放到記憶體中去,然後再對比較大的表格進行map操作。join就發生在map操作的時候,每當掃描一個大的table中的資料,就要去去檢視小表的資料,哪條與之相符,繼而進行連線。這裡的join並不會涉及reduce操作。map端join的優勢就是在於沒有shuffle,真好。在實際的應用中,我們這樣設定:
***1. set hive.auto.convert.join=true;
這樣設定,hive就會自動的識別比較小的表,繼而用mapJoin來實現兩個表的聯合。看看下面的兩個表格的連線。

<property>
  <name>hive.auto.convert.join.noconditionaltask.size</name>
  <value>10000000</value> The default is 10MB
 </property>

DistributedCache是分散式快取的一種實現,它在整個MapReduce框架中起著相當重要的作用,他可以支撐我們寫一些相當複雜高效的分散式程

這裡的第一句話就是執行本地的map join任務,繼而轉存檔案到XXX.hashtable下面,在給這個檔案裡面上傳一個檔案進行map join,之後才運行了MR程式碼去執行計數任務。說白了,在本質上mapjoin根本就沒有執行MR程序,僅僅是在記憶體就進行了兩個表的聯合。

mapjoin使用場景
1.關聯操作中有一張表非常小
2.不等值的連結操作

自動執行

set hive.auto.convert.join=true;
hive.mapjoin.smalltable.filesize=25;預設值是25mb   
<property>
  <name>hive.mapjoin.smalltable.filesize</name>
  <value>25000000</value>
 </property>

手動執行 A為小表 如果A表超過25M,還想使用map join;
select /+mapjoin(A)/ f.a,f.b from A t join B f on(f.a==t.a)

Reduce-side Join

***hive join操作預設使用的就是reduce join
Reduce-side Join原理上要簡單得多,它也不能保證相同key但分散在不同dataset中的資料能夠進入同一個Mapper,整個資料集合的排序
在Mapper之後的shuffle過程中完成。相對於Map-side Join,它不需要每個Mapper都去讀取所有的dataset,這是好處,但也有壞處,
即這樣一來Mapper之後需要排序的資料集合會非常大,因此shuffle階段的效率要低於Map-side Join。
***reduce side join是一種最簡單的join方式,其主要思想如下:
在map階段,map函式同時讀取兩個檔案File1和File2,為了區分兩種來源的key/value資料對,對每條資料打一個標籤(tag)

semi join 小表對大表 是reudce join的變種 map階段過濾掉不需要join的欄位 相當於Hivw SQL加的where過濾

SMB Join(sort merge bucket)

SMB 存在的目的主要是為了解決大表與大表間的 Join 問題,分桶其實就是把大表化成了“小表”,然後 Map-Side Join 解決之,這是典型的分而治之的思想。

set hive.enforce.bucketing=true;
set hive.enforce.sorting=true;

表優化資料目標:相同資料儘量聚集在一起