Hive基礎
1、引入原因
對存在HDFS上的檔案或HBase中的表進行查詢時,是要手工寫一堆MapReduce代碼
對于統計任務,隻能由懂MapReduce的程式員才能搞定
事實上,許多底層細節實際上進行的是從一個任務到下一個任務的重複性工作
使用MapReduce的時候遇到複雜的統計邏輯,這種MapReduce任務需要等上一個任務跑完再接下一個任務,而判斷一個任務是否跑完,則是通過檢測HDFS上對應輸出檔案是否生成_SUCCESS檔案來判斷,然後利用shell腳本去把它們串起來,整個流程就很費時費力,而使用hive的sql形式則會相對來說更簡單。
Hive不僅提供了一個熟悉SQL的使用者所能快速使用熟悉的程式設計模型,還消除了大量的通用代碼, 讓開發者可以花費很少的精力就完成大量的工作
2、hive是什麼
- Hive是一個SQL解析引擎,将SQL語句轉譯成MR Job,然後再在Hadoop平台上運作,達到快速開發的目的。
- Hive中的表是純邏輯表,就隻是表的定義等,即表的中繼資料。本質就是Hadoop的目錄/檔案, 達到了中繼資料與資料存儲分離的目的
- Hive本身不存儲資料,它完全依賴HDFS和MapReduce。
- Hive的内容是讀多寫少,不支援對資料的改寫和删除
Hive從0.14版本後已經可以修改更新了,不過這個功能一般預設關閉的,也都是針對與内部表資料
- Hive中沒有定義專門的資料格式,由使用者指定,需要指定三個屬性:列分隔符、行分隔符、讀取檔案資料的方法
常見的列分隔符:空格、 、01
常見的行分隔符:
讀取檔案資料方法:TextFile、SequenceFile(二進制)、RCFile
通常都會先對要統計的資料提前做處理,将内容中可能會出現的分隔符先處理掉,防止處理資料的時候因為内容中包含對應分隔符而導緻資料丢失,分隔符一般是需要打日志的時候大家約定好,是以不同公司的分割符都各有差別。
- 注:SequenceFile(二進制):是hadoop提供的一種二進制檔案,以<k,v>形式序列化到檔案中,java Writeable 接口進行序列化和反序列化。
- 注:RCFile是Hive專門推出的,一種面向列的資料格式。
總結:Hive是基于Hadoop的一個資料倉庫工具,可以将結構化的資料檔案映射為一張表,并提供類SQL查詢功能。本質是将HQL轉化成MapReduce程式:1、Hive處理的資料存儲在HDFS,2、Hive分析資料底層的實作是MapReduce,3、執行程式運作在YARN上。
Hive的優缺點
優點:
1)操作接口采用類SQL文法,提供快速開發的能力(簡單、容易上手)
2)避免了去寫MapReduce,減少開發人員的學習成本。
3)Hive的執行延遲比較高,是以Hive常用于對實時性要求不高的場合的資料分析;
4)Hive優勢在于處理大資料,對于處理小資料沒有優勢
5)Hive支援使用者自定義函數,使用者可以根據自己的需求來實作自己的函數。
缺點:
1)Hive的HQL表達能力有限
(1)疊代式算法無法表達
(2)資料挖掘方面不擅長
2)Hive的效率比較低
(1)Hive自動生成的MapReduce作業,通常情況下不夠智能化
(2)Hive調優比較困難,粒度較粗
3、Java和Hive:詞頻統計算法
Java:
package org.myorg;
import java.io.IOException;
import java.util.*;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
public class WordCount {
public static classMapextendsMapper<LongWritable, Text, Text,
IntWritable>
private final static IntWritable one = newIntWritable(1);
private Text word = newText();
public void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
StringTokenizer tokenizer = new String Tokenizer(line);.
while (tokenizer.hasMoreTokens()) {
word.set(tokenizer.nextToken());
context.write(word, one);
}
}
}
public static class Reduce extends Reducer<Text, IntWritable, Text,
IntWritable> {
public void reduce(Text key, Iterable<IntWritable> values, Context
context)
throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val
values) {
sum += val.get();
context.write(key, new IntWritable(sum));
public static void main (String[]args)throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, "wordcount");
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setMapperClass(Map.class);
job.setReducerClass(Reduce.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
FileInputFo rmat.addInputPath(job, new Path(args[0]));
File0utputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}
}
}
hive:
create table docs(line String);
load data inpath 'docs' overwrite into table docs;
create table word_counts as
select word,count(1) as cnt
from (select explode(split(line,'s')) as word from docs) w
group by word
order by word;
上述兩個例子都是盡可能簡單的方法将檔案中的内容分割成單詞,也就是按照空格進行劃分的。借助java API可以定制和調整一個算法實作的每個細節,不過大多數情況下,使用者都不需要這個級别的控制,而hive則可以更容易快速的實作大多數情況的需求。
4、Hive中的SQL與傳統SQL區 别
HQL | SQL | |
---|---|---|
資料存儲 | HDFS、Hbase | Local FS |
資料格式 | 使用者自定義 | 系統決定 |
資料更新 | 不支援(把之前的資料覆寫) | 支援 |
索引 | 有(0.8版之後增加) | 有 |
執行 | MapReduce(select * from table) | Executor |
執行延遲 | 高 | 低 |
可擴充性 | 高(UDF、UDAF,UDTF) | 低 |
資料規模 | 大(資料大于TB) | 小 |
資料檢查 | 讀時模式 | 寫時模式 |
- hive和關系資料庫存儲檔案的系統不同,hive使用的是hadoop的HDFS(hadoop的分布式檔案系統),關系資料庫則是伺服器本地的檔案系統;
- hive使用的計算模型是mapreduce,而關系資料庫則是自己設計的計算模型;
- 關系資料庫都是為實時查詢的業務進行設計的,而hive則是為海量資料做資料挖掘設計的,實時性很差
- Hive很容易擴充自己的存儲能力和計算能力,這個是繼承hadoop的,而關系資料庫在這個方面要比Hive差很多
UDF:直接應用于select語句,通常查詢的時候,需要對字段做一些格式化處理(如:大小寫轉換)《特點:一進一出,一比一的關系。》
UDAF:多對一的場景,如聚合時
UDTF:一對多的場景
以上都是使用者自定義函數
讀時模式:隻有在讀的時候才會檢查、解析字段和schema(資料結構表達)優點:加載資料的時候非常迅速,因為在寫的過程中是不需要解析資料
寫時模式:則是在寫入的時候就會檢查解析等,缺點:寫的慢,需要建立索引、壓縮、資料一緻性、字段檢查等等 優點:讀的時候會得到優化
5、Hive體系架構
1)使用者接口:Client
CLI(hive shell)、JDBC/ODBC(Hive的用戶端,使用者通過java連接配接至Hive Server)、WEBUI(浏覽器通路hive,在公司一般通過遊覽器的方式操作)
2)中繼資料:Metastore
中繼資料包括:表名、表所屬的資料庫(預設是default)、表的擁有者、列/分區字段、表的類型(是否是外部表)、表的資料所在目錄等;
預設存儲在自帶的derby資料庫中,推薦使用MySQL存儲Metastore
3)Hadoop
使用HDFS進行存儲,使用MapReduce進行計算。
Hive資料以檔案形式存儲在HDFS的指定目錄下
Hive語句生成查詢計劃,由MapReduce調用執行
4)驅動器:Driver
(1)解析器(SQL Parser):将SQL字元串轉換成抽象文法樹AST,這一步一般都用第三方工具庫完成,比如antlr;對AST進行文法分析,比如表是否存在、字段是否存在、SQL語義是否有誤。
(2)編譯器(Physical Plan):将AST編譯生成邏輯執行計劃。
(3)優化器(Query Optimizer):對邏輯執行計劃進行優化。
(4)執行器(Execution):把邏輯執行計劃轉換成可以運作的實體計劃。對于Hive來說,就是MR/Spark。
6、Hive執行流程
1)流程概述
完整流程:通過UI或者Client的形式送出任務(使用JDBC的形式,隻是多了一層Thrift Server,它們三種形式本質上都是一樣的),首先使用者的executeQuery(查詢指令),會由driver進行解析(解析過程為:driver會将執行的語句先交給compiler(解析器)生成抽象文法樹,檢查SQL文法是否正确即getPlan,然後通過metastore(中繼資料存儲,一般存儲在mysql裡面)getMetaData(擷取中繼資料資訊),用來檢查語句中的表是否存在,再将檢查資訊sendMetaData(發送中繼資料資訊)到compiler,compiler會綜合檢查資訊,sendPlan到driver。)如果語句解析都沒問題,driver則通過将語句整理為executePlan(執行計劃)到executionEngine(執行引擎),由executionEngine送出MR任務到Hadoop的JobTracker,同時也會通過metastore擷取對應的中繼資料資訊,再由Hadoop來執行相應的MR任務後再将結果傳回到executionEngine,executionEngine再将結果sendResults(發送結果)到driver,driver最後整理結果fetchResults(擷取結果)到任務送出端。
簡述:Hive通過給使用者提供的一系列互動接口,接收到使用者的指令(SQL),使用自己的Driver,結合中繼資料(MetaStore),将這些指令翻譯成MapReduce,送出到Hadoop中執行,最後,将執行傳回的結果輸出到使用者互動接口。
Hive 在執行一條 HQL 的時候,會經過以下步驟:
1、文法解析:Antlr 定義 SQL 的文法規則,完成 SQL 詞法,文法解析,将 SQL 轉化為抽象文法樹 AST Tree;
2、語義解析:周遊 AST Tree,抽象出查詢的基本組成單元 QueryBlock;
3、生成邏輯執行計劃:周遊 QueryBlock,翻譯為執行操作樹 OperatorTree,其中是一個個操作符Operator;
操作符 Operator 是 Hive 的最小處理單元,每個操作符代表一個 HDFS 操作或者 MapReduce 作業
4、優化邏輯執行計劃:邏輯層優化器進行 OperatorTree 變換,合并不必要的 ReduceSinkOperator,減少 shuffle 資料量;
5、生成實體執行計劃:周遊 OperatorTree,翻譯為 MapReduce 任務;
6、優化實體執行計劃:實體層優化器進行 MapReduce 任務的變換,生成最終的執行計劃。
最後Hive 通過 ExecMapper 和 ExecReducer 執行 MapReduce 程式,執行模式有本地模式和分布式兩種模式
2)Hive操作符清單
操作符 | 描述 |
---|---|
TableScanOperator | 掃描hive表資料 |
ReduceSinkOperator | 建立将發送到Reducer端的<Key,Value>對 |
JoinOperator | Join兩份資料 |
SelectOperator | 選擇輸出列 |
FileSinkOperator | 建立結果資料,輸出至檔案 |
FilterOperator | 過濾輸入資料 |
GroupByOperator | Group By語句 |
MapJoinOperator | /* + mapjoin(t) */ |
LimitOperator | Limit語句 |
UnionOperator | Union語句 |
3)Hive 編譯器的工作職責
(1)Parser:将 HQL 語句轉換成抽象文法樹(AST:Abstract Syntax Tree)
(2)Semantic Analyzer:将抽象文法樹轉換成查詢塊
(3)Logic Plan Generator:将查詢塊轉換成邏輯查詢計劃
(4)Logic Optimizer:重寫邏輯查詢計劃,優化邏輯執行計劃
(5)Physical Plan Gernerator:将邏輯計劃轉化成實體計劃(MapReduce Jobs)
(6)Physical Optimizer:選擇最佳的 Join 政策,優化實體執行計劃
4)優化器類型
名稱 | 作用 |
---|---|
② SimpleFetch0pt imizer | 優化沒有GroupBy表達式的聚合查詢 |
② MapJoinProcessor | MapJoin,需要SQL中提供hint, 0. 11版本已不用 |
② BucketMapJoinOptimizer | BucketMapJoin |
② GroupByOptimizer | Map端聚合 |
① ReduceSinkDeDupl ication | 合并線性的OperatorTree中partition/sort key 相同的reduce |
① PredicatePushDown | 謂詞前置 |
① CorrelationOptimizer | 利用查詢中的相關性,合并有相關性的Job,HIVE- -2206 |
ColumnPruner | 字段剪枝 |
注:上表中帶①符号的,優化目的都是盡量将任務合并到一個 Job 中,以減少 Job 數量,帶②的優化目的是盡量減少 shuffle 資料量
5)Join實作過程
SELECT pv.pageid, u.age FROM page_view pv JOIN user u ON pv.userid = u.userid;
對于上述 join 操作實作過程:
Map:
1、以 JOIN ON 條件中的列作為 Key,如果有多個列,則 Key 是這些列的組合
2、以 JOIN 之後所關心的列作為 Value,當有多個列時,Value 是這些列的組合。在 Value 中還會包含表的 Tag 資訊,用于标明此 Value 對應于哪個表
3、按照 Key 進行排序
Shuffle:
1、根據 Key 的值進行 Hash,并将 Key/Value 對按照 Hash 值推至不同對 Reduce 中
Reduce:
1、 Reducer 根據 Key 值進行 Join 操作,并且通過 Tag 來識别不同的表中的資料
6)GroupBy實作過程
SELECT pageid, age, count(1) FROM pv_users GROUP BY pageid, age;
對于上述 group by 操作實作過程:
7)Distinct實作過程
按照 age 分組,然後統計每個分組裡面的不重複的 pageid 有多少個
SELECT age, count(distinct pageid) FROM pv_users GROUP BY age;
對于上述 distinct操作實作過程如下圖,該 SQL 語句會按照 age 和 pageid 預先分組,進行 distinct 操作。然後會再按 照 age 進行分組,再進行一次 distinct 操作
7、Hive資料管理
hive的表本質就是Hadoop的目錄/檔案
hive預設表存放路徑一般都是在你工作目錄的hive目錄裡面,按表名做檔案夾分開,如果你有分區表的話,分區值是子檔案夾,可以直接在其它的M/R job裡直接應用這部分資料
Name | HDFS Directory | |
---|---|---|
Table | mobile_user | /lbs/mobile_user |
Partition | action = insight, dt= 20131020 pc m app | /lbs/mobile_user/action=insight/dt=20131020 |
Bucket | clusted by user into 32 buckets | /lbs/mobile_user/action=insight/dt=20131020/part-00031 |
1)Hive資料類型
Hive 表中的列支援以下基本資料類型:《數倉中最常用的是string和DECIMAL》
大類 | 類型 |
---|---|
Integers(整型) | TINYINT—1 位元組的有符号整數SMALLINT—2 位元組的有符号整數INT—4 位元組的有符号整數BIGINT—8 位元組的有符号整數 |
Boolean(布爾型) | BOOLEAN—TRUE/FALSE |
Floating point numbers(浮點型) | FLOAT— 單精度浮點型DOUBLE—雙精度浮點型 |
Fixed point numbers(定點數) | DECIMAL—使用者自定義精度定點數,比如 DECIMAL(7,2) |
String types(字元串) | STRING—指定字元集的字元序列VARCHAR—具有最大長度限制的字元序列CHAR—固定長度的字元序列 |
Date and time types(日期時間類型) | TIMESTAMP — 時間戳TIMESTAMP WITH LOCAL TIME ZONE — 時間戳,納秒精度DATE—日期類型 |
Binary types(二進制類型) | BINARY—位元組序列 |
Hive 資料類型 | Java 資料類型 | 長度 | 例子 |
---|---|---|---|
TINYINT | byte | 1byte 有符号整數 | 20 |
SMALINT | short | 2byte 有符号整數 | 20 |
INT | int | 4byte 有符号整數 | 20 |
BIGINT | long | 8byte 有符号整數 | 20 |
BOOLEAN | boolean | 布爾類型,true 或者 false | TRUE FALSE |
FLOAT | float | 單精度浮點數 | 3.14159 |
DOUBLE | double | 雙精度浮點數 | 3.14159 |
STRING | string | 字元系列。可以指定字元集。可以使用單引号或者雙引号。 | ‘now is the time’ “for all good men” |
TIMESTAMP | 時間類型 | ||
BINARY | 位元組數組 |
注:
對于 Hive 的 String 類型相當于資料庫的 varchar 類型,該類型是一個可變的字元串,不過它不能聲明其中最多能存儲多少個字元,理論上它可以存儲 2GB 的字元數。
TIMESTAMP 和 TIMESTAMP WITH LOCAL TIME ZONE 的差別如下:
1、TIMESTAMP WITH LOCAL TIME ZONE:使用者送出時間給資料庫時,會被轉換成資料庫所在的時區來儲存。查詢時則按照查詢用戶端的不同,轉換為查詢用戶端所在時區的時間。
2、TIMESTAMP :送出什麼時間就儲存什麼時間,查詢時也不做任何轉換。
複雜類型:
類型 | 描述 | 示例 |
---|---|---|
STRUCT | 類似于對象,是字段的集合,字段的類型可以不同,可以使用《名稱.字段名》方式進行通路 | STRUCT ('xiaoming', 12 , '2018-12-12') |
MAP | 鍵值對的集合,可以使用《名稱[key]》的方式通路對應的值 | map('a', 1, 'b', 2) |
ARRAY | 數組是一組具有相同類型和名稱的變量的集合,這些變量稱為數組的元素,每個數組元素都有一個編号,編号從零開始。可以使用《名稱[index]》通路對應的值 | ARRAY('a', 'b', 'c', 'd') |
示例:
如下給出一個基本資料類型和複雜資料類型的使用示例:
1)建立本地測試檔案 test.txt
songsong,bingbing_lili,xiao song:18_xiaoxiao song:19,hui long guan_beijing yangyang,caicai_susu,xiao yang:18_xiaoxiao yang:19,chao yang_beijing
注意:MAP,STRUCT 和 ARRAY 裡的元素間關系都可以用同一個字元表示,這裡用“_”。
2)Hive 上建立測試表 test
create table test(
name string,
friends array<string>,
children map<string, int>,
address struct<street:string, city:string>)
row format delimited
fields terminated by ','
collection items terminated by '_'
map keys terminated by ':'
lines terminated by '
';
字段解釋:
row format delimited fields terminated by ',' -- 列分隔符
collection items terminated by '_' --MAP STRUCT 和 ARRAY 的分隔符(資料分割符号)<這意味着map,struct,array等的分隔符必須保持一緻>
map keys terminated by ':' -- MAP 中的 key 與 value 的分隔符
lines terminated by '
'; -- 行分隔符<這個預設是/n,可以不寫>
3)導入文本資料到測試表
hive (default)>load data local inpath "/opt/module/datas/test.txt" into table test;
4)通路三種集合列裡的資料,以下分别是 ARRAY,MAP,STRUCT 的通路方式
hive (default)>select friends[1],children['xiao song'],address.city from test where name="songsong";
OK
_c0 _c1 city lili 18 beijing
Time taken: 0.076 seconds, Fetched: 1 row(s)
2)Hive存儲格式
Hive資料以檔案形式存儲在HDFS的指定目錄下
Hive語句生成查詢計劃,由MR調用執行
檔案存儲的格式:
1.textfile
預設格式,建表時不指定預設為這個格式
存儲方式:行存儲
優點:可以直接讀取
缺點:磁盤開銷大 資料解析開銷大。壓縮的text檔案 hive無法進行合并和拆分
2.sequencefile
二進制檔案,以<key,value>的形式序列化到檔案中
存儲方式:行存儲
缺點:存儲空間消耗最大
優點:可分割 壓縮,全表時查詢效率高
一般選擇block壓縮,檔案和Hadoop api中的mapfile是互相相容的。EQUENCEFILE将資料以<key,value>的形式序列化到檔案中。序列化和反序列化使用Hadoop 的标準的Writable 接口實作。key為空,用value 存放實際的值, 這樣可以避免map 階段的排序過程。三種壓縮選擇:NONE, RECORD, BLOCK。 Record壓縮率低,一般建議使用BLOCK壓縮。使用時設定參數,
SET hive.exec.compress.output=true;
SET io.seqfile.compression.type=BLOCK; -- NONE/RECORD/BLOCK
create table test2(str STRING)
STORED AS SEQUENCEFILE;
3.rcfile
一種行列存儲相結合的存儲方式。首先,其将資料按行分塊,保證同一個record在一個塊上,避免讀一個記錄需要讀取多個block。其次,塊資料列式存儲,有利于資料壓縮和快速的列存取。 理論上具有高查詢效率(但hive官方說效果不明顯,隻有存儲上能省10%的空間,是以不好用,可以不用)。
RCFile結合行存儲查詢的快速和列存儲節省空間的特點
1)同一行的資料位于同一節點,是以元組重構的開銷很低;
- 塊内列存儲,可以進行列次元的資料壓縮,跳過不必要的列讀取。
查詢過程中,在IO上跳過不關心的列。實際過程是,在map階段從遠端拷貝仍然拷貝整個資料塊到本地目錄,也并不是真正直接跳過列,而是通過掃描每一個row group的頭部定義來實作的。但是在整個HDFS Block 級别的頭部并沒有定義每個列從哪個row group起始到哪個row group結束。是以在讀取所有列的情況下,RCFile的性能反而沒有SequenceFile高。
優點:壓縮快, 快速列存取, 讀記錄盡量涉及到的block最少 ,讀取需要的列隻需要讀取每個row group 的頭部定義。
缺點:讀取全量資料的操作 性能可能比sequencefile沒有明顯的優勢。但是如果指定一列的話,效率最高
4.orc
存儲方式:資料按行分塊 每塊按照列存儲
壓縮快 快速列存取
效率比rcfile高,是rcfile的改良版本
5.自定義格式
使用者可以通過實作inputformat和 outputformat來自定義輸入輸出格式。
總結
textfile 存儲空間消耗比較大,并且壓縮的text 無法分割和合并 查詢的效率最低,可以直接存儲,加載資料的速度最高
sequencefile 存儲空間消耗最大,壓縮的檔案可以分割和合并 查詢效率高,需要通過text檔案轉化來加載
rcfile 存儲空間最小,查詢的效率最高 ,需要通過text檔案轉化來加載,加載的速度最低
注:hive預設本地資料庫(用來存儲中繼資料):derby(單使用者模式常用),而一般開發是用mysql(多使用者模式、遠端服務模式)
指定存儲格式
通常在建立表的時候使用 stored as 參數指定:
create table 表名(字段名 類型,字段名 類型)
row format delimited
fields terminated by 字段分隔符
lines terminated by 列分隔符
STORED AS 存儲格式;
2)類型轉換
Hive 的原子資料類型是可以進行隐式轉換的,類似于 Java 的類型轉換,例如某表達式使用 INT 類型,TINYINT 會自動轉換為 INT 類型,但是 Hive 不會進行反向轉化,例如,某表達式使用 TINYINT 類型,INT 不會自動轉換為 TINYINT 類型,它會傳回錯誤,除非使用 CAST 操作。
隐式轉換
Hive 中基本資料類型遵循以下的層次結構,按照這個層次結構,子類型到祖先類型允許隐式轉換。例如 INT 類型的資料允許隐式轉換為 BIGINT 類型。額外注意的是:按照類型層次結構允許将 STRING 類型隐式轉換為 DOUBLE 類型。
隐式類型轉換規則如下
(1)任何整數類型都可以隐式地轉換為一個範圍更廣的類型,如 TINYINT 可以轉換成 INT,INT 可以轉換成 BIGINT。
(2)所有整數類型、FLOAT 和 STRING 類型<内容必須是數值>都可以隐式地轉換成 DOUBLE。
(3)TINYINT、SMALLINT、INT 都可以轉換為 FLOAT。
(4)BOOLEAN 類型不可以轉換為任何其它的類型。
CAST 操作
可以使用 CAST 操作顯示進行資料類型轉換
例如 CAST('1' AS INT)将把字元串'1' 轉換成整數 1;
如果強制類型轉換失敗,如執行CAST('X' AS INT),表達式傳回空值 NULL。
8、Hive的四種資料模型
1)資料表
Table(内部表)
一般說的hive表都是指内部表,預設建立的表都是所謂的内部表,有時也被稱為管理表。(因為這種表,Hive 會(或多或少地)控制着資料的生命周期。管理表不适合和其他工具共享資料。)Hive中的内部表在HDFS中都有相應的目錄用來存儲表的資料,目錄可以通過${HIVE_HOME}/conf/hive-site.xml配置檔案中的 hive.metastore.warehouse.dir屬性來配置,一般預設的值是/user/hive/warehouse(這個目錄在 HDFS上),如果我有一個表test,那麼在HDFS中會建立/user/hive/warehouse/test目錄(這裡假定hive.metastore.warehouse.dir配置為/user/hive/warehouse);test表所有的資料都存放在這個目錄中,當然,外部表可以配置其它hdfs來映射檔案。可以使用如下指令來檢視表對應hdfs的檔案:dfs -ls /hive/warehouse/ods_uba.db/test;
External table(外部表)
Hive中的外部表和表很類似,隻不過是建表時可以指定加載的hdfs目錄,也可以不指定後頭根據需要再進行加載。如果外部表使用hive指令删除表,對應的hdfs檔案是不會被删除。外部表比較靈活,不止可以關聯到hdfs檔案,也可以關聯到hbse表。其建表和内部表稍有不同,但是可用的資料類型都是一樣的。因為當表被設定是外部表, Hive就認為并非其完全擁有這份資料。删除該表不會删除掉這份資料,隻會将描述表的中繼資料資訊會被删除掉。
内部表和外部表的差別:
(1)、外部表建立時要添加EXTERNAL,外部表查詢是隻是去關聯hdfs檔案,并按照分割符号轉成對應的字段
(2)、外部表删除表後,hdfs檔案不會被删除。同理,外部表删除分區後,hdfs檔案也不會被删除,針對誤操作提高了容錯。
(3)、内部表删除時候,不僅表結構會被删除,資料也會被删除,沒法恢複,而外部表删除後重建立立時,資料就自動恢複了,不會真的删除掉資料,針對誤操作提高了容錯。
2)分區表
Partition(分區表)
在Hive中,Partition表中的一個Partition對應于表下的一個目錄,所有的Partition的資料都存儲在對應的目錄中。分區表主要是為了輔助查詢,縮小查詢範圍,加快資料的檢索速度和對資料按照一定的規格和條件進行管理。《工作中常見的是分區表,日期(date),按照天分區;來源(source),三端app,m(mobile手機端頁面,一般是分享頁面),pc》——什麼時候采用分區?主要是結合業務,經常要用到的分析條件(業内術語一般稱“口徑”)在where條件裡面經常要被用到的,可以按照條件進行設計分區。(設計表之後也要盡量根據資料來優化業務表,提高資料使用效率!)
分區表實際上就是對應一個HDFS檔案系統上的獨立的檔案夾,該檔案夾下是該分區所有的資料檔案。Hive中的分區就是分目錄,把一個大的資料集根據業務需要分割成小的資料集。在查詢時通過 WHERE 子句中的表達式選擇查詢所需要的指定的分區,這樣的查詢效率會提高很多。
Bucket(分桶表)
在Hive中,table可以拆分成partition,而table和partition還可以通過‘CLUSTERED BY’進一步拆分,即分桶,bucket中的資料可以通過SORT BY排序;
set hive.enforce.bucketing = true可以自動控制上一輪的reduce的數量進而适配bucket的個數,當然,使用者也可以自主設定mapred.reduce.tasks去适配bucket個數;分桶的原理就是對指定的列計算其hash,根據hash值切分資料,目的是為了并行,每一個桶對應一個檔案(注意和分區的差別)。
比如将lin_test表start_time列分散至16個桶中,首先對id列的值計算hash,
對應hash值為0和16的資料存儲的HDFS目錄為:/user/hive/warehouse/lin_test/start_date=20191218/part-00000;
而hash值為2的資料存儲的HDFS 目錄為:/user/hive/warehouse/start_date=20191218/part-00002。
bucket的重要作用是:資料sampling(采樣),提升某些查詢操作的效率,例如mapside join。不過一般情況下不建議将分桶設定太大,以免小檔案過多引起其它更多的問題,用好分桶才能真的有助于提高計算的效率。
hive>select * from student tablesample(bucket 1 out of 2 on id);
tablesample是抽樣語句,文法:tablesample(bucket x out of y)
y一般是table總bucket數的倍數或者因子。hive根據y的大小決定抽樣的比例,用總bucket數除y的值,即得到需要抽樣的個數,x則代表從第幾個bucket開始抽取,每次抽取間隔的bucket數就是y的值。
分區提供一一個隔離資料和優化查詢的便利的方式。不過,并非所有的資料集都可形成合理的分區,特别是之前所提到過的要确定合适的劃分大小這個疑慮。分桶是将資料集分解成更容易管理的若幹部分的另一個技術。例如,假設有個表的一級分區是dt,代表日期,二級分區是user_ id, 那麼這種劃分方式可能會導緻太多的小分區。回想下,如果使用者是使用動态分區來建立這些分區的話,那麼預設情況下,Hive會限制動态分區可以建立的最大分區數,用來避免由于建立太多的分區導緻超過了檔案系統的處理能力以及其他一些問題。是以,如下指令可能會執行失敗:
hive> CREATE TABLE weblog (url STRING,source_ ip STRING)
hive> PARTITIONED BY (dt STRING, user_ id INT) ;
hive> FROM raw_ weblog
hive> INSERT OVERWRITE TABLE page_ view PARTITION(dt='2020-06-08', user_ id)
hive> SELECT server_ name, url, source_ ip, dt, user_ id;
不過,如果我們對表weblog進行分桶,并使用user_ id 字段作為分桶字段,則字段值會根據使用者指定的值進行哈希分發到桶中。同一個user_ id下的記錄通常會存儲到同一個桶内。假設使用者數要比桶數多得多,那麼每個桶内就将會包含多個使用者的記錄:
hive> CREATE TABLE weblog (user_ id INT, url STRING, source_ ip STRING)
hvie> PARTITIONED BY (dt STRING)
hvie> CLUSTERED BY (user_ id) INTO 96 BUCKETS;
不過,将資料正确地插人到表的過程完全取決于使用者自己。CREATE TABLE語句中所規定的資訊僅僅定義了中繼資料,而不影響實際填充表的指令。下面介紹如何在使用INSERT ... TABLE語句時,正确地填充表。首先,我們需要設定一個屬性來強制Hive 為目标表的分桶初始化過程設定-一個 正确的reducer 個數。然後我們再執行一個查詢來填充分區。例如:
hive> SET hive. enforce .bucketing = true;
hive> FROM raw_ logs
hive> INSERT OVERWRITE TABLE weblog
hive> PARTITION (dt='2009-02-25' )
hive> SELECT user_ id, url, source_ ip WHERE dt='2020-02-25';
如果我們沒有使用hive.enforce. bucketing屬性,那麼我們就需要自己設定和分桶個數相比對的reducer個數,例如,使用set mapred.reduce.tasks=96,然後在INSERT語句中的SELECT語句後增加CLUSTER BY語句。
分桶優點
1、因為桶的數量是固定的,是以它沒有資料波動。桶對于抽樣再合适不過。如果兩個表都是按照user_ id 進行分桶的話,那麼Hive可以建立一個邏輯上正确的抽樣。
2、分桶有利于執行高效的map-side JOIN:如果所有表中的資料是分桶的,那麼對于大表,在特定的情況下同樣可以使用這個優化。簡單地說,表中的資料必須是按照ON語句中的鍵進行分桶的,而且其中一張表的分桶的個數必須是另一張表分桶個數的若幹倍。當滿足這些條件時,那麼Hive可以在map階段按照分桶資料進行連接配接。是以這種情況下,不需要先擷取到表中所有的内容,之後才去和另一張表中每個分桶進行比對連接配接。這個優化同樣預設是沒有開啟的。需要設定參數hive.optimize.bucketmapJOIN為true才可以開啟此優化:
set hive.opt imize.bucke tmapJOIN=true;
如果所涉及的分桶表都具有相同的分桶數,而且資料是按照連接配接鍵或桶的鍵進行排序的,那麼這時Hive可以執行一個更快的分類-合并連接配接(sort-merge JOIN)。同樣地,這個優化需要需要設定如下屬性才能開啟:
set hive.input. format=org.apache.hadoop. hive.ql.io.Bucketi zedHiveInputFormat;
set hive.optimi ze.bucketmapj oin=true;
set hive.optimize.bucketmapj oin. sortedmerge=true;