Hive理論基礎

但求大資料開發一職發表於2020-10-10
數倉特徵:面向主題,整合,非易失的,時變。資料倉儲是在資料庫已經大量存在的情況下,為了進一步挖掘資料資源、為了決策需要而產生的,不是所謂的“大型資料庫”。
 
資料庫與資料倉儲的區別(OLTP 與 OLAP 的區別)
  操作型處理,叫聯機事務處理 OLTP(On-Line Transaction Processing,),也可以稱面向交易的處理系統,它是針對具體業務在資料庫聯機的日常操作,通常對少數記錄進行查詢、修改。使用者較為關心操作的響應時間、資料的安全性、完整性和併發支援的使用者數等問題。傳統的資料庫系統作為資料管理的主要手段,主要用於操作型處理。
  分析型處理,叫聯機分析處理 OLAP(On-Line Analytical Processing)一般針對某些主題的歷史資料進行分析,支援管理決策。
 
ETL:抽取 Extra, 轉化 Transfer, 裝載 Load。
 
為什麼要對數倉分層?
分層:Ods、Dw、Dm、Ads
  用空間換時間,通過大量的預處理來提升應用系統的使用者體驗(效率),因此資料倉儲會存在大量冗餘的資料;不分層的話,如果源業務系統的業務規則發生變化將會影響整個資料清洗過程,工作量巨大。
 
數倉後設資料管理
  後設資料(Meta Date), 主要記錄資料倉儲中模型的定義、各層級間的對映關係、監控資料倉儲的資料狀態及 ETL 的任務執行狀態。可分為技術後設資料和業務後設資料。
  後設資料不僅定義了資料倉儲中資料的模式、來源、抽取和轉換規則等,而且是整個資料倉儲系統執行的基礎,後設資料把資料倉儲系統中各個鬆散的元件聯絡起來,組成了一個有機的整體。
 
Hive 是基於 Hadoop 的一個資料倉儲工具,可以將結構化的資料檔案對映為一張資料庫表,並提供類 SQL 查詢功能。 本質是將 SQL 轉換為 MapReduce 程式。利用HDFS 儲存資料,利用 MapReduce 查詢分析資料。
 

元件:使用者介面,後設資料儲存mysql / derby,解釋、編譯、優化、執行器。

 

與資料庫的區別

  1. 資料儲存位置不同:Hive儲存在HDFS中,資料庫儲存在塊裝置或本地檔案
  2. 資料更新:數倉一般不改寫資料,資料庫增刪改查
  3. 執行延遲:Hive延遲高, mysql延遲低, 只有大規模資料時Hive平行計算的優點才會體現
  4. 資料規模:Hive大規模計算,資料庫規模較小
 
三種配置模式(本地模式、遠端模式與mysql是否在遠端無關!!!)
  1. 內嵌模式:使用的是內嵌的Derby資料庫來儲存後設資料,也不需要額外起Metastore服務。
  1. 本地模式:
  本地模式採用外部資料庫來儲存後設資料,目前支援的資料庫有:MySQL、Postgres、Oracle、MS SQL Server.
  不需要單獨起metastore服務,用的是跟hive在同一個程式裡的metastore服務。也就是說當你啟動一個hive 服務,裡面預設會幫我們啟動一個metastore服務。hive根據hive.metastore.uris 引數值來判斷,如果為空,則為本地模式。
  缺點:每啟動一次hive服務,都內建啟動了一個metastore。本地模式下hive的配置主需要指定mysql的相關資訊即可。(ConnectionURL)
  1. 遠端模式:
  需要單獨起metastore服務,然後每個客戶端都在配置檔案裡配置連線到該metastore服務。遠端模式的metastore服務和hive執行在不同的程式裡。
  在生產環境中,建議用遠端模式來配置Hive Metastore。其他依賴hive的軟體都可以通過Metastore訪問hive。
  遠端模式下,需要配置hive.metastore.uris 引數來指定metastore服務執行的機器ip和埠,並且需要單獨手動啟動metastore服務。

 
資料模型
  • db(庫):在 hdfs 中表現為 hive.metastore.warehouse.dir 目錄下一個資料夾
  • table(內部表):在 hdfs 中表現所屬 db 目錄下一個資料夾,當我們刪除一個內部表時,Hive也會刪除這個表中資料。內部表不適合和其他工具共享資料。
  • external table(外部表):資料存放位置可以在 HDFS 任意指定路徑 ,刪除該表並不會刪除掉原始資料,刪除的是表的後設資料
  • partition(分割槽):在 hdfs 中表現為 table 目錄下的子目錄

DDL操作:
 1 create table t_user_part(id int,name string,country string) 
 2 partitioned by (guojia string) 
 3 row format delimited fields terminated by ',' ;
 4 --注意順序問題
 5 --分割槽的欄位不能是表當中的欄位
 6 
 7 load data local inpath './root/4.txt' 
 8 into table t_user_part partition (guojia='usa');
 9 
10 load data local inpath '/root/5.txt' 
11 into table t_user_part partition (guojia='china');
12  --將資料載入到哪個資料夾中
13  
14  --多級分割槽
15 create table t_order(id int,pid int,price double) 
16 partitioned by (year string,month string,day string) 
17 row format delimited fields terminated by ',' ; 
18 
19 load data local inpath '/root/5.txt' 
20 into table t_order partition (year='2019',month='09',day='18');
21 
22 load data local inpath '/root/4.txt' 
23 into table t_order partition (year='2019',month='09',day='18');
24 
25 ALTER TABLE t_user_part ADD PARTITION (guojia='riben') 
26 location '/user/hive/warehouse/hadoop32.db/t_user_part/guojia=riben'; 
27 --一次新增一個分割槽
28 
29 ALTER TABLE order ADD 
30 PARTITION (year='2018', month='09',day="20") 
31 location'/user/hive/warehouse/hadoop32.db/t_order' 
32 PARTITION (year='2019', month='09',day="20") 
33 location'/user/hive/warehouse/hadoop32.db/t_order';  
34  --一次新增多個分割槽
35  
36 --刪除分割槽
37 ALTER TABLE t_user_part DROP IF EXISTS PARTITION (guojia=riben);
38 
39 --檢視分割槽
40 show partitions table_name;
41 
42 show formatted table_name;
  • bucket(分桶):在 hdfs 中表現為同一個表目錄下根據 hash 雜湊之後的多個檔案 ,採用對列值雜湊,然後除以桶的個數求餘的方式決定該條記錄存放在哪個桶當中
DDL操作:
 1 create table stu_buck(Sno string,Sname string,
 2 Sbrithday string, Sex string)
 3 clustered by(Sno) 
 4 into 4 buckets
 5 row format delimited fields terminated by '\t';
 6 --clustered by 根據哪個欄位去分桶,這個欄位在表中一定存在
 7 --into N buckets 分多少個檔案
 8 --如果該分桶欄位是string,會根據字串的hashcode % bucketsNum
 9 --如果該分桶欄位是數值型別,數值 % bucketsNum
10 
11 create table student(Sno string,Sname string,
12 Sbrithday string, Sex string) 
13 row format delimited fields terminated by '\t';
14 --insert+select
15 insert overwrite table stu_buck select * from student 
16 cluster by(Sno);
17 --預設不讓直接使用分桶表
 
DML操作
 1 --load載入 推薦方式,最常見  (分桶表是不支援load)
 2 load data local inpath '/root/hivedata/students.txt' 
 3 overwrite into table student;
 4 --載入本地資料到表對應的路徑下
 5 --local表明是本地還是hdfs
 6 --overwrite表示覆蓋操作(慎用)
 7 
 8 load data inpath '/stu' into table student_ext;
 9 --載入hdfs上的檔案到表對應的路徑下(追加)
10 
11 --insert + select匯入
12 --insert 主要是結合 select 查詢語句使用,將查詢結果插入到表中
13 insert overwrite table tablename1 
14 [partition (partcol1=val1,partclo2=val2)] 
15 select_statement1 from source_table 
16 
17 --多重插入
18 from source_table 
19 insert overwrite table tablename1 
20 [partition (partcol1=val1,partclo2=val2)] 
21 select_statement1 
22 insert overwrite table tablename2 
23 [partition (partcol1=val1,partclo2=val2)] 
24 select_statement2.. 
25 
26 --動態插入 substr(day,1,7) as month,day分割槽的虛擬欄位 順序需要對應
27 insert overwrite table d_p_t partition (month,day) 
28 select ip,substr(day,1,7) as month,day 
29 from dynamic_partition_table;
30 
31 --指定分隔符(複雜型別的資料表)
32 --表1(包含array欄位型別)
33 --資料: zhangsan    beijing,shanghai,tianjin,hangzhou
34 --       wangwu    shanghai,chengdu,wuhan,haerbin
35 create table complex_array(name string,
36 work_locations array<string>) 
37 row format delimited fields terminated by '\t' 
38 collection items terminated by ',';
39 --collection items array集合分隔符
40 
41 --表2(包含map欄位型別)
42 create table t_map(id int,name string,hobby map<string,string>)
43 row format delimited 
44 fields terminated by ','
45 collection items terminated by '-'
46 map keys terminated by ':' ;
47 --map keys map中k-v分隔符
48 --資料:1,zhangsan,唱歌:非常喜歡-跳舞:喜歡-游泳:一般般
49 --      2,lisi,打遊戲:非常喜歡-籃球:不喜歡
 
DQL操作
4個By區別
  Sort By:分割槽內有序,只保證每個 reducer 的輸出有序,不保證全域性有序。
  Order By:全域性排序,只有一個Reducer;
  Distrbute By:類似MR中Partition,進行分割槽,結合sort by使用。
  Cluster By:當Distribute by和Sorts by欄位相同時可以使用Cluster by方式。Cluster by除了具有Distribute by的功能外還兼具Sort by的功能。但是排序只能是升序排序,不能指定排序規則為ASC或者DESC。
  如果 distribute 和 sort 的欄位是同一個時,此時,cluster by = distribute by + sort by
 
Join
  inner join 內連線,兩張表都滿足條件的資料
  left join 左連結,以左表為主表,主表的資料都顯示
  left semi join 顯示左表的資料部分(內連線)
 
引數的配置方式優先順序別:依次增強
  預設的配置(hive-default.xml),自定義的配置(hive-site.xml),shell命令列引數,session的命令列中進行設定
Shell命令列引數(常用)   -e "sql" 可以跟上sql的字串,-f file.sql 可以跟上sql指令碼檔案
            -hiveconf <property = value> (引數配置,傳遞引數到指令碼檔案中)
            -hivevar <key = value> (只能傳遞引數)
 
內建函式
  檢視系統自帶的函式:show functions;
  顯示自帶的函式的用法:
    #不詳細 desc function upper;
    #詳細 desc function extended upper;
條件判斷函式: CASE
語法 : CASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END
返回值 : T
說明:如果 a 等於 b ,那麼返回 c ;如果 a 等於 d ,那麼返回 e ;否則返回 f
舉例:hive> Select case 100 when 50 then 'tom' when 100 then 'mary' else 'tim' end from dual;
mary
字串連線函式:CONCAT
帶分隔符字串連線函式:concat_ws
舉例:select concat_ws(',', 'abc', '123')
 
自定義函式

  UDF(User-Defined-Function)普通函式 一進一出

繼承UDF
過載evaluate方法
打成jar包(胖包)上傳到伺服器 
將jar包新增到 hive 的 classpath 
    hive>add jar /home/hadoop/udf.jar; 
建立臨時函式與開發好的java class關聯 
    create temporary function tolowercase as 
    'cn.itcast.hive.UDF_Demo'; 
(不加temporary就是建立永久函式,需要使用drop手動刪除)
在hql中使用自定義的函式tolowercase ip  
    Select tolowercase(name),age from t_test; 

  UDAF(User-Defined Aggregation Function)聚合函式 多進一出

UDAF是輸入多個資料行,產生一個資料行
使用者自定義的UDAF必須是繼承了UDAF,且內部包含多個實現了exec的靜態類

  UDTF(User-Defined Table-Generating Functions)表生成函式 一進多出

繼承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF,
實現initialize, process, close三個方法。
UDTF首先會呼叫initialize方法,
    此方法返回UDTF的返回行的資訊(返回個數,型別)。
初始化完成後,會呼叫process方法,真正的處理過程在process函式中,
    在process中,每一次forward()呼叫產生一行;
    如果產生多列可以將多個列的值放在一個陣列中,
    然後將該陣列傳入到forward()函式。
最後close()方法呼叫,對需要清理的方法進行清理
把程式打成jar包
新增jar包:add jar /run/jar/udf_test.jar;
建立臨時函式:
    CREATE TEMPORARY FUNCTION explode_map 
    AS 'cn.itcast.hive.udtf.ExplodeMap';
銷燬臨時函式:hive> DROP TEMPORARY FUNCTION add_example;

UDTF有兩種使用方法,
    一種直接放到select後面(不可以新增其他欄位使用,不可以巢狀呼叫,
        不可以和group by/cluster by/distribute by/sort by一起使用)
    一種和lateral view一起使用
 
lateral view(側檢視)與 explode函式

explode可以對數值型別為array,或者為map結構的進行分割處理
  對array處理:將array每個元素單獨作為一行輸出
  對map處理:將map中的元素作為一行輸出,key作為一列,value作為一列
一般情況下,直接使用即可,也可以根據需要結合lateral view 使用
lateral view為側檢視,意義是為了配合UDTF來使用,把某一行資料拆分成多行資料。不加lateral view的UDTF只能提取單個欄位拆分,並不能塞回原來資料表中。加上lateral view就可以將拆分的單個欄位資料與原始表資料關聯上。在使用lateral view的時候需要指定檢視別名和生成的新列別名。
 
1 --select 欄位1, 欄位2, ... 
2 --from tabelA lateral view UDTF(xxx) 檢視別名(虛擬表名) as a,b,c
3 --例如
4 select name,subview.* from test_message 
5 lateral view explode(location) subview as lc;
 
行列轉換
1.多行轉多列
  col1 col2 col3
  a      c      1
  a      d      2
  a      e      3
  b      c      4
  b      d      5
  b      e      6
現在要將其轉化為:
  col1 c d e
  a     1 2 3
  b     4 5 6
此時需要使用到max(case … when … then … else 0 end),僅限於轉化的欄位為數值型別且為正值的情況
1 select col1,
2 max(case col2 when 'c' then col3 else 0 end) as c,
3 max(case col2 when 'd' then col3 else 0 end) as d,
4 max(case col2 when 'e' then col3 else 0 end) as e
5 from row2col
6 group by col1;
 
2.多行轉單列(重要)
  col1 col2 col3
  a      b      1
  a      b      2
  a      b      3
  c      d      4
  c      d      5
  c      d      6
將其轉化為:
  col1 col2 col3
  a      b     1,2,3
  c      d     4,5,6
此時需要兩個內建的函式:
  a)concat_ws(引數1,引數2),用於進行字元的拼接
    引數1—指定分隔符
    引數2—拼接的內容
  b)collect_set(col3),它的主要作用是將某欄位的值進行去重彙總,產生array型別欄位,如果不想去重可用collect_list()
 1 select collect_set(col3) from row2col_1;
 2 --將col3的所有資料放到一個集合中(去重)
 3 
 4 select collect_set(col3) from row2col_1 group by col1,col2;
 5 --根據col1,col2進行分組,只有第一列和第二列都相同,認為是同一組
 6 
 7 select col1,col2, collect_set(col3) from row2col_1 
 8 group by col1,col2;
 9 --三列顯示,行轉列
10 
11 select col1, col2, 
12 concat_ws('', collect_set(cast(col3 as string))) as col3
13 from row2col_1
14 group by col1, col2;
15 --cast(col3 as string)將第三列變成string型別
16 --因為concat_ws是對於字串拼接
 
3.多列轉多行
col1 c d e
a     1 2 3
b     4 5 6
現要將其轉化為:
col1 col2 col3
a      c      1
a      d      2
a      e      3
b      c      4
b      d      5
b      e      6
這裡需要使用union進行拼接。union 可以結合多個select語句 返回共同的結果集保證每個select語句返回的資料型別個數是一致的。
1 select col1, 'c' as col2, c as col3 from col2row
2 UNION
3 select col1, 'd' as col2, d as col3 from col2row
4 UNION
5 select col1, 'e' as col2, e as col3 from col2row
6 order by col1, col2;
 
4.單列轉多行(重要)
col1 col2 col3
a      b     1,2,3
c      d     4,5,6
現要將其轉化為:
col1 col2 col3
a      c      1
a      d      2
a      e      3
b      c      4
b      d      5
b      e      6
這裡需要使用UDTF(表生成函式)explode(),該函式接受array型別的引數,其作用恰好與collect_set相反,實現將array型別資料行轉列。explode配合lateral view實現將某列資料拆分成多行。
1   select col1, col2, lv.col3 as col3
2   from col2row_2 
3   lateral view explode(split(col3, ',')) lv as col3;
 
reflect函式
  可以支援在 sql 中呼叫 java 中的自帶函式,秒殺一切 udf 函式
--例1
--使用 java.lang.Math 當中的 Max 求兩列當中的最大值 
select reflect("java.lang.Math","max",col1,col2) from test_udf; 

--例2
--準備資料 test_udf2.txt 
java.lang.Math,min,1,2 
java.lang.Math,max,2,3 
--執行查詢 
select reflect(class_name,method_name,col1,col2) from test_udf2; 
 
json
什麼叫json:原生的js物件
hive處理json資料總體來說有兩個方向的路走:
1. 將json以字串的方式整個匯入hive表,然後通過使用UDF函式解析已經匯入到hive中的資料,比如使用lateral view json_tuple的方法,獲取所需要的列名
  • get_json_object(string json_string,string path):第一個引數填寫json物件變數,第二個引數使用$表示json變數表示,每次只能返回一個資料項
1 select get_json_object(t.json,'$.id'),
2 get_json_object(t.json,'$.total_number') 
3 from tmp_json_test t;
  • json_tuple(string json_string,'屬性1','屬性2')
1 select json_tuple(json,'id','ids','total_number') 
2 from tmp_json_test;
2. 在匯入之前將json拆成各個欄位,匯入Hive表的資料是已經解析過的,這將需要使用地方放的SerDe
 1 --從http:www.congiu.net/hive-json-serde/下載jar包
 2 add jar 
 3 /root/hivedata/json-serde-1.3.7-jar-with-dependencies.jar;
 4 
 5 create table tmp_json_array(id string,
 6 ids array<string>,total_number int) 
 7 row format SERDE 'org.openx.data.jsonserde.JsonSerDe' 
 8 stored as textfile;
 9 load data local inpath '/root/hivedata/json_test.txt' 
10 overwrite into table tmp_json_array;
 
視窗函式
又叫 OLAP 函式/分析函式,兼具分組和排序功能
視窗函式最重要的關鍵字是 partition by 和 order by。
具體語法如下:over (partition by xxx order by xxx)
  - 如果不指定 rows between,預設為從起點到當前行;
  - 如果不指定 order by,則將分組內所有值累加;
  - 關鍵是理解 rows between 含義,也叫做 window 子句:
    - preceding:往前
    - following:往後
    - current row:當前行
    - unbounded:起點
    - unbounded preceding 表示從前面的起點
    - unbounded following:表示到後面的終點
AVG,MIN,MAX,和 SUM 用法一樣。
例:
 1 select cookieid,createtime,pv, 
 2 sum(pv) over(partition by cookieid order by createtime) as pv1  
 3 from itcast_t1; 
 4 --pv1: 分組內從起點到當前行的 pv 累積,
 5 --如,11 號的 pv1=10 號的 pv+11 號的 pv, 12 號=10 號+11 號+12 
 6 
 7 select cookieid,createtime,pv, 
 8 sum(pv) over(partition by cookieid) as pv3 
 9 from itcast_t1; 
10 --pv3: 分組內(cookie1)所有的 pv 累加 
11 
12 select cookieid,createtime,pv, 
13 sum(pv) over(partition by cookieid 
14 order by createtime 
15 rows between 3 preceding and 1 following) as pv5 
16 from itcast_t1;
17 --pv5: 分組內當前行+往前 3 行+往後 1 行,
18 --如,14 號=11 號+12 號+13 號+14 號+15 號=5+7+3+2+4=21 
19 
20 select cookieid,createtime,pv, 
21 sum(pv) over(partition by cookieid 
22 order by createtime rows between current row and 
23 unbounded following) as pv6 
24 from itcast_t1; 
25 --pv6: 分組內當前行+往後所有行,
26 --如,13 號=13 號+14 號+15 號+16 號=3+2+4+4=13,
27 --14 號=14 號+15 號+16 號=2+4+4=10 
  • ROW_NUMBER() 從 1 開始,按照順序,生成分組內記錄的序列。 1 2 3 4
  • RANK() 生成資料項在分組中的排名,排名相等會在名次中留下空位 。1 2 2 4
  • DENSE_RANK()生成資料項在分組中的排名,排名相等在名次中不會留下空位。1 2 2 3

1 SELECT  
2 cookieid, 
3 createtime, 
4 pv, 
5 RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1, 
6 DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2, 
7 ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3  
8 FROM itcast_t2  WHERE cookieid = 'cookie1'; 
NTILE
有時會有這樣的需求:如果資料排序後分為三部分,業務人員只關心其中的一部分,如何將這中間的三分之一資料拿出來呢?NTILE 函式即可以滿足。 可以看成是:把有序的資料集合平均分配到指定的數量(num)個桶中, 將桶號分配給每一行。如果不能平均分配,則優先分配較小編號的桶,並且各個
桶中能放的行數最多相差 1。 然後可以根據桶號,選取前或後 n 分之幾的資料。資料會完整展示出來,只是給相應的資料打標籤;具體要取幾分之幾的資料,需要再巢狀一層根據標籤取出。
 1 SELECT * FROM
 2 (SELECT  
 3 cookieid, 
 4 createtime, 
 5 pv, 
 6 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn1, 
 7 NTILE(3) OVER(PARTITION BY cookieid ORDER BY createtime) AS rn2, 
 8 NTILE(4) OVER(ORDER BY createtime) AS rn3 
 9 FROM itcast_t2  ORDER BY cookieid,createtime) temp
10 WHERE cookieid = 'cookie2' AND  rn2 = 2;

Lag(col, n)往前n行

Lead(col, n)往後n行

 
資料壓縮
優缺點
  優點: 減少儲存磁碟空間,降低單節點的磁碟 IO。 由於壓縮後的資料佔用的頻寬更少,因此可以快資料在 Hadoop 叢集流動的速度,減少網路傳輸頻寬。
  缺點: 需要花費額外的時間/CPU 做壓縮和解壓縮計算
MR哪些過程可以設定壓縮?
  需要分析處理的資料在進入map 前可以壓縮,然後解壓處理,map 處理完成後的輸出可以壓縮,這樣可以減少網路 I/O(reduce 通常和 map 不在同一節點上),reduce 拷貝壓縮的資料後進行解壓,處理完成後可以壓縮儲存在 hdfs 上,以減少磁碟佔用量。
 
資料儲存格式
  • 行式儲存
  優點: 相關的資料是儲存在一起,比較符合物件導向的思維,因為一行資料就是一條記錄,這種儲存格式比較方便進行 INSERT/UPDATE 操作
  缺點: 如果查詢只涉及某幾個列,它會把整行資料都讀取出來,不能跳過不必要的列讀取。當然資料比較少,一般沒啥問題,如果資料量比較大就比較影響效能 由於每一行中,列的資料型別不一致,導致不容易獲得一個極高的壓縮比,也就是空間利用率不高 不是所有的列都適合作為索引
  • 列式儲存
  優點: 查詢時,只有涉及到的列才會被查詢,不會把所有列都查詢出來,即可以跳過不必要的列查詢; 高效的壓縮率,不僅節省儲存空間也節省計算記憶體和 CPU。任何列可以作為索引;
  缺點: INSERT/UPDATE 很麻煩或者不方便; 不適合掃描小量的資料
Hive 支援的儲存數的格式主要有:TEXTFILE(行式儲存) 、SEQUENCEFILE(行式儲存)、ORC(列式儲存)、PARQUET(列式儲存)。
 

TEXTFILE,行式儲存,但使用這種方式,hive 不會對資料進行切分,從而無法對資料進行並行操作

 
ORC,列式儲存,它並不是一個單純的列式儲存格式,仍然是首先根據行組分割整個表,在每一個行組內進行按列儲存
  優點: ORC 是列式儲存,有多種檔案壓縮方式,並且有著很高的壓縮比。 檔案是可切分(Split)的。因此,在 Hive 中使用 ORC 作為表的檔案儲存格式,不僅節省 HDFS 儲存資源,查詢任務的輸入資料量減少,使用的 MapTask 也就減少了。 ORC 可以支援複雜的資料結構(比如 Map 等)。ORC 檔案也是以二進位制方式儲存的,所以是不可以直接讀取,ORC 檔案也是自解析的。
一個 ORC 檔案可以分為若干個 Stripe,一個 Stripe可以分為三個部分:
  1. indexData:某些列的索引資料。一個輕量級的 index,預設是每隔 1W 行做一個索引。這裡做的索引只是記錄某行的各欄位在 Row Data 中的 offset
  2. rowData :真正的資料儲存。,先取部分行,然後對這些行按列進行儲存。對每個列進行了編碼,分成多個 Stream 來儲存。
  3. StripFooter:存放各個stripe 的後設資料資訊。每個檔案有一個 File Footer,這裡面存的是每個 Stripe 的行數,每個 Column的資料型別資訊等;每個檔案的尾部是一個 PostScript,這裡面記錄了整個檔案的壓縮型別以及 FileFooter 的長度資訊等。在讀取檔案時,會 seek 到檔案尾部讀PostScript,從裡面解析到 File Footer 長度,再讀 FileFooter,從裡面解析到各個Stripe 資訊,再讀各個 Stripe,即從後往前讀。

PARQUET,列式儲存,是面向分析型業務的列式儲存格式。Parquet 檔案是以二進位制方式儲存的,所以是不可以直接讀取的,檔案中包括該檔案的資料和後設資料,因此 Parquet 格式檔案是自解析的。 通常情況下,在儲存Parquet資料的時候會按照Block大小設定行組的大小,由於一般情況下每一個 Mapper 任務處理資料的最小單位是一個 Block,這樣可以把每一個行組由一個 Mapper 任務處理,增大任務執行並行度。

 
儲存格式總結
  ORC儲存檔案預設採用 ZLIB 壓縮。比 snappy 壓縮的小。 在實際的專案開發當中,hive 表的資料儲存格式一般選擇:orc 或 parquet。壓縮方式一般選擇 snappy。
  儲存檔案的壓縮比總結: ORC > Parquet > textFile
  儲存檔案查詢速度三種差不多

 
優化
0. 分割槽分桶技術,行列過濾
1. Fetch 抓取機制
在 hive-default.xml.template 檔案中 hive.fetch.task.conversion 預設是 more,老版本 hive 預設是 minimal,該屬性修改為 more 以後,在全域性查詢、欄位查詢、limit 查詢等都不走 mapreduce。
2. mapreduce 本地模式
mapreduce可以使用本地模擬環境執行,此時就不是分散式執行的程式,但是針對小檔案小資料處理特別有效果。使用者可以通過設定 hive.exec.mode.local.auto 的值為 true,來讓 Hive 在適當的時候自動啟動這個優化。
3. join優化
  1)map join 在 Reduce 階段完成 join。容易發生資料傾斜。可以用 MapJoin 把小表全部載入到記憶體在 map 端進行 join,避免 reducer處理。 在實際使用中,只要根據業務把握住小表的閾值標準即可,hive 會自動幫我們完成 mapjoin,提高執行的效率。
  2)大表 join 大表
空key過濾,key對應的資料為異常資料,例如空,可進行過濾
空key轉換,key對應的資料有用,必須進行join,通過 hive 的 rand 函式,隨記的給每一個為空的 id 賦上一個隨機值,這樣就不會造成資料傾斜。
  3)大小表,小大表join 在當下的 hive 版本中,大表 join 小表或者小表 join 大表,就算是關閉 map端 join 的情況下,基本上沒有區別了(hive 為了解決資料傾斜的問題,會自動進行過濾) 。
4. group by 優化—map 端聚合
很多聚合操作都可以先在 Map 端進行部分聚合,最後在 Reduce 端得出最終結果。
  1)是否在 Map 端進行聚合,預設為 True set hive.map.aggr = true;
  2)在 Map 端進行聚合操作的條目數目 set hive.groupby.mapaggr.checkinterval = 100000;
  3)有資料傾斜的時候進行負載均衡(預設是 false) set hive.groupby.skewindata = true;
5. 資料傾斜問題
  1)調整mapTask個數
在Map執行前合併小檔案,減少Map數:CombineHiveInputFormat具有對小檔案進行合併的功能(系統預設的格式)。HiveInputFormat沒有對小檔案合併功能。
當 input 的檔案都很大,任務邏輯複雜,map 執行非常慢的時候,可以考慮增加 Map 數
  2)調整reduceTask個數,reduce 個數並不是越多越好
    1)過多的啟動和初始化 reduce 也會消耗時間和資源;
    2)另外,有多少reduce,就會有多少輸出檔案,如果生成很多個小檔案,那麼如果這些小檔案作為下一個任務的輸入,則也會出現小檔案過多的問題; 在設定 reduce 個數的時候也需要考慮這兩個原則:處理大資料量利用合適的 reduce 數;使單個 reduce 任務處理資料量大小要合適。
6. 瞭解執行計劃—explain
7. 並行執行機制
通過設定引數 hive.exec.parallel 值為true,就可以開啟併發執行。
8. 嚴格模式
通過設定屬性 hive.mapred.mode 值為預設是非嚴格模式 nonstrict 。開啟嚴格模式需要修改 hive.mapred.mode 值為 strict,開啟嚴格模式可以禁止 3 種型別的查詢。
  1)對於分割槽表,除非 where 語句中含有分割槽欄位過濾條件來限制範圍,否
則不允許執行。使用者不允許掃描所有分割槽。
  2)對於使用了 order by 語句的查詢,要求必須使用 limit 語句。因為 order
by 為了執行排序過程會將所有的結果資料分發到同一個 Reducer 中進行處理,
  3)限制笛卡爾積的查詢。
9. jvm 重用機制
JVM 重用可以使得 JVM 例項在同一個 job 中重新使用 N 次,這個功能的缺點是,開啟 JVM 重用將一直佔用使用到的 task 插槽,以便進行重用,直到任務完成後才能釋放。
10. 推測執行機制
推測出“拖後腿”的任務,併為這樣的任務啟動一個備份任務,讓該任務與原始任務同時處理同一份資料,並最終選用最先成功執行完成任務的計算結果作為最終結果。

相關文章