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. 推測執行機制
推測出「拖後腿」的任務,並為這樣的任務啟動一個備份任務,讓該任務與原始任務同時處理同一份數據,並最終選用最先成功運行完成任務的計算結果作為最終結果。