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中沒有定義專門的數據格式,由用戶指定,需要指定三個屬性:列分隔符、行分隔符、讀取文件數據的方法
常見的列分隔符:空格、\t、\001
常見的行分隔符:\n
讀取文件數據方法: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 '\n';
字段解釋:
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'; -- 行分隔符<這個默認是/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;