Hive體系結構

Hive是一種建立在hadoop上的數據倉庫架構,可以用來進行數據ETL,並對存儲在hdfs中的數據進行查詢、分析和管理。
1、Hive架構與基本組成
下面是Hive的架構圖。
這裏寫圖片描述
圖1.1 Hive體系結構
Hive的體系結構可以分爲以下幾部分:
(1)用戶接口主要有三個:CLI,Client 和 WUI。其中最常用的是CLI,Cli啓動的時候,會同時啓動一個Hive副本。Client是Hive的客戶端,用戶連接至Hive Server。在啓動 Client模式的時候,需要指出Hive Server所在節點,並且在該節點啓動Hive Server。 WUI是通過瀏覽器訪問Hive。
(2)Hive將元數據存儲在數據庫中,如mysql、derby。Hive中的元數據包括表的名字,表的列和分區及其屬性,表的屬性(是否爲外部表等),表的數據所在目錄等。
(3)解釋器、編譯器、優化器完成HQL查詢語句從詞法分析、語法分析、編譯、優化以及查詢計劃的生成。生成的查詢計劃存儲在HDFS中,並在隨後有MapReduce調用執行。
(4)Hive的數據存儲在HDFS中,大部分的查詢、計算由MapReduce完成
有三種模式可以連接到數據庫:
(1) 單用戶模式。此模式連接到一個In-memory 的數據庫Derby,一般用於Unit Test。
(2)多用戶模式。通過網絡連接到一個數據庫中,是最經常使用到的模式。
(3) 遠程服務器模式。用於非Java客戶端訪問元數據庫,在服務器端啓動MetaStoreServer,客戶端利用Thrift協議通過MetaStoreServer訪問元數據庫。
對於數據存儲,Hive沒有專門的數據存儲格式,也沒有爲數據建立索引,用戶可以非常自由的組織Hive中的表,只需要在創建表的時候告訴Hive數據中的列分隔符和行分隔符,Hive就可以解析數據。Hive中所有的數據都存儲在HDFS中,存儲結構主要包括數據庫、文件、表和視圖。Hive中包含以下數據模型:Table內部表,External Table外部表,Partition分區,Bucket桶。Hive默認可以直接加載文本文件,還支持sequence file 、RCFile。
Hive的數據模型介紹如下:
(1)Hive數據庫
類似傳統數據庫的DataBase,在第三方數據庫裏實際是一張表。簡單示例命令行 hive > create database test_database;
(2)內部表
Hive的內部表與數據庫中的Table在概念上是類似。每一個Table在Hive中都有一個相應的目錄存儲數據。例如一個表pvs,它在HDFS中的路徑爲/wh/pvs,其中wh是在hive-site.xml中由${hive.metastore.warehouse.dir} 指定的數據倉庫的目錄,所有的Table數據(不包括External Table)都保存在這個目錄中。刪除表時,元數據與數據都會被刪除。
內部表簡單示例:
創建數據文件:test_inner_table.txt
創建表:create table test_inner_table (key string)
加載數據:LOAD DATA LOCAL INPATH ‘filepath’ INTO TABLE test_inner_table
查看數據:select * from test_inner_table; select count(*) from test_inner_table
刪除表:drop table test_inner_table
(3)外部表
外部表指向已經在HDFS中存在的數據,可以創建Partition。它和內部表在元數據的組織上是相同的,而實際數據的存儲則有較大的差異。內部表的創建過程和數據加載過程這兩個過程可以分別獨立完成,也可以在同一個語句中完成,在加載數據的過程中,實際數據會被移動到數據倉庫目錄中;之後對數據對訪問將會直接在數據倉庫目錄中完成。刪除表時,表中的數據和元數據將會被同時刪除。而外部表只有一個過程,加載數據和創建表同時完成(CREATE EXTERNAL TABLE ……LOCATION),實際數據是存儲在LOCATION後面指定的 HDFS 路徑中,並不會移動到數據倉庫目錄中。當刪除一個External Table時,僅刪除該鏈接。
外部表簡單示例:
創建數據文件:test_external_table.txt
創建表:create external table test_external_table (key string)
加載數據:LOAD DATA INPATH ‘filepath’ INTO TABLE test_inner_table
查看數據:select * from test_external_table; •select count(*) from test_external_table
刪除表:drop table test_external_table
(4)分區
Partition對應於數據庫中的Partition列的密集索引,但是Hive中Partition的組織方式和數據庫中的很不相同。在Hive中,表中的一個Partition對應於表下的一個目錄,所有的Partition的數據都存儲在對應的目錄中。例如pvs表中包含ds和city兩個Partition,則對應於ds = 20090801, ctry = US 的HDFS子目錄爲/wh/pvs/ds=20090801/ctry=US;對應於 ds = 20090801, ctry = CA 的HDFS子目錄爲/wh/pvs/ds=20090801/ctry=CA。
分區表簡單示例:
創建數據文件:test_partition_table.txt
創建表:create table test_partition_table (key string) partitioned by (dt string)
加載數據:LOAD DATA INPATH ‘filepath’ INTO TABLE test_partition_table partition (dt=‘2006’)
查看數據:select * from test_partition_table; select count(*) from test_partition_table
刪除表:drop table test_partition_table

(5)桶
Buckets是將表的列通過Hash算法進一步分解成不同的文件存儲。它對指定列計算hash,根據hash值切分數據,目的是爲了並行,每一個Bucket對應一個文件。例如將user列分散至32個bucket,首先對user列的值計算hash,對應hash值爲0的HDFS目錄爲/wh/pvs/ds=20090801/ctry=US/part-00000;hash值爲20的HDFS目錄爲/wh/pvs/ds=20090801/ctry=US/part-00020。如果想應用很多的Map任務這樣是不錯的選擇。
桶的簡單示例:
創建數據文件:test_bucket_table.txt
創建表:create table test_bucket_table (key string) clustered by (key) into 20 buckets
加載數據:LOAD DATA INPATH ‘filepath’ INTO TABLE test_bucket_table
查看數據:select * from test_bucket_table; set hive.enforce.bucketing = true;
(6)Hive的視圖
視圖與傳統數據庫的視圖類似。視圖是隻讀的,它基於的基本表,如果改變,數據增加不會影響視圖的呈現;如果刪除,會出現問題。•如果不指定視圖的列,會根據select語句後的生成。
示例:create view test_view as select * from test

2、Hive的執行原理
這裏寫圖片描述
圖2.1 Hive的執行原理

Hive構建在Hadoop之上,
(1)HQL中對查詢語句的解釋、優化、生成查詢計劃是由Hive完成的
(2)所有的數據都是存儲在Hadoop中
(3)查詢計劃被轉化爲MapReduce任務,在Hadoop中執行(有些查詢沒有MR任務,如:select * from table)
(4)Hadoop和Hive都是用UTF-8編碼的
2.1 執行的過程:
HiveQL通過CLI/web UI或者thrift 、 odbc 或 jdbc接口的外部接口提交,經過complier編譯器,運用Metastore中的元數據進行類型檢測和語法分析,生成一個邏輯方案(logical plan),然後通過簡單的優化處理,產生一個以有向無環圖DAG數據結構形式展現的map-reduce任務。
2.2 查詢編譯器(query complier)
用雲存儲中的元數據來生成執行計劃,步驟如下:
1).解析(parse)-anlr解析其生成語法樹AST(hibernate也是這個):將HQL轉化爲抽象語法樹AST
2).類型檢查和語法分析(type checking and semantic analysis):將抽象語法樹轉換此查詢塊(query block tree),並將查詢塊轉換成邏輯查詢計劃(logic plan Generator);
3).優化(optimization):重寫查詢計劃(logical optimizer)–>將邏輯查詢計劃轉成物理計劃(physical plan generator)–>選擇最佳的join策略(physical optimizer)
3、Hive和數據庫的異同

由於Hive採用了SQL的查詢語言HQL,因此很容易將Hive理解爲數據庫。其實從結構上來看,Hive和數據庫除了擁有類似的查詢語言,再無類似之處。數據庫可以用在Online的應用中,但是Hive是爲數據倉庫而設計的,清楚這一點,有助於從應用角度理解Hive的特性。
Hive和數據庫的比較如下表:

這裏寫圖片描述

(1)查詢語言。由於 SQL 被廣泛的應用在數據倉庫中,因此專門針對Hive的特性設計了類SQL的查詢語言HQL。熟悉SQL開發的開發者可以很方便的使用Hive進行開發。
(2)數據存儲位置。Hive是建立在Hadoop之上的,所有Hive的數據都是存儲在HDFS中的。而數據庫則可以將數據保存在塊設備或者本地文件系統中。
(3)數據格式。Hive中沒有定義專門的數據格式,數據格式可以由用戶指定,用戶定義數據格式需要指定三個屬性:列分隔符(通常爲空格、”\t”、”\x001″)、行分隔符(”\n”)以及讀取文件數據的方法(Hive中默認有三個文件格式TextFile,SequenceFile以及RCFile)。由於在加載數據的過程中,不需要從用戶數據格式到Hive定義的數據格式的轉換,因此,
Hive在加載的過程中不會對數據本身進行任何修改,而只是將數據內容複製或者移動到相應的HDFS目錄中。
而在數據庫中,不同的數據庫有不同的存儲引擎,定義了自己的數據格式。所有數據都會按照一定的組織存儲,因此,數據庫加載數據的過程會比較耗時。
(4)數據更新。由於Hive是針對數據倉庫應用設計的,而數據倉庫的內容是讀多寫少的。因此,Hive中不支持對數據的改寫和添加,所有的數據都是在加載的時候中確定好的。而數據庫中的數據通常是需要經常進行修改的,因此可以使用INSERT INTO … VALUES添加數據,使用UPDATE … SET修改數據。
(6)執行。Hive中大多數查詢的執行是通過Hadoop提供的MapReduce來實現的(類似select * from tbl的查詢不需要MapReduce)。而數據庫通常有自己的執行引擎。
(7)執行延遲。之前提到,Hive在查詢數據的時候,由於沒有索引,需要掃描整個表,因此延遲較高。另外一個導致Hive執行延遲高的因素是MapReduce框架。由於MapReduce本身具有較高的延遲,因此在利用MapReduce執行Hive查詢時,也會有較高的延遲。相對的,數據庫的執行延遲較低。當然,這個低是有條件的,即數據規模較小,當數據規模大到超過數據庫的處理能力的時候,Hive的並行計算顯然能體現出優勢。
(8)可擴展性。由於Hive是建立在Hadoop之上的,因此Hive的可擴展性是和Hadoop的可擴展性是一致的(世界上最大的Hadoop集羣在Yahoo!,2009年的規模在4000臺節點左右)。而數據庫由於ACID語義的嚴格限制,擴展行非常有限。目前最先進的並行數據庫Oracle在理論上的擴展能力也只有100臺左右。
(9)數據規模。由於Hive建立在集羣上並可以利用MapReduce進行並行計算,因此可以支持很大規模的數據;對應的,數據庫可以支持的數據規模較小。
4、Hive元數據庫
Hive將元數據存儲在RDBMS 中,一般常用的有MYSQL和DERBY
這裏寫圖片描述
從上面表的內容來看,hive整個創建表的過程已經比較清楚了。
(1)解析用戶提交hive語句,對其進行解析,分解爲表、字段、分區等hive對象
(2)根據解析到的信息構建對應的表、字段、分區等對象,從 SEQUENCE_TABLE中獲取構建對象的最新ID,與構建對象信息(名稱,類型等)一同通過DAO方法寫入到元數據表中去,成功後將SEQUENCE_TABLE中對應的最新ID+5。
5、Hive基本操作
Create Table語句的一些注意項:
(1)CREATE TABLE創建一個指定名字的表。如果相同名字的表已經存在,則拋出異常;用戶可以用IF NOT EXIST選項來忽略這個異常。
(2)EXTERNAL 關鍵字可以讓用戶創建一個外部表,在建表的同時指定一個指向實際數據的路徑( LOCATION ),Hive 創建內部表時,會將數據移動到數據倉庫指向的路徑;若創建外部表,僅記錄數據所在的路徑,不對數據的位置做任何改變。在刪除表的時候,內部表的元數據和數據會被一起刪除,而外部表只刪除元數據,不刪除數據。
(3)LIKE允許用戶複製現有的表結構,但是不復制數據。
(4)用戶在建表的時候可以自定義SerDe或者使用自帶的 SerDe ( Serialize/Deserilize 的簡稱,目的是用於序列化和反序列化 )。如果沒有指定 ROW FORMAT 或者 ROW FORMAT DELIMITED,將會使用自帶的SerDe。在建表的時候,用戶還需要爲表指定列,用戶在指定表的列的同時也會指定自定義的SerDe,Hive通過SerDe確定表的具體的列的數據。
(5)如果文件數據是純文本,可以使用STORED AS TEXTFILE。如果數據需要壓縮,使用STORED AS SEQUENCE。
(6)有分區的表可以在創建的時候使用 PARTITIONED B Y語句。一個表可以擁有一個或者多個分區,每一個分區單獨存在一個目錄下。而且,表和分區都可以對某個列進行CLUSTERED BY操作,將若干個列放入一個桶(bucket)中。也可以利用SORT BY對數據進行排序。這樣可以爲特定應用提高性能。
(7)表名和列名不區分大小寫,SerDe和屬性名區分大小寫。表和列的註釋是字符串。
Alter Table語句:主要功能包括Add Partitions, Drop Partitions, Rename Table, Change Column, Add/Replace Columns。
Create View語句:創建視圖。格式CREATE VIEW [IF NOT EXISTS] view_name [ (column_name [COMMENT column_comment], …) ]
Showy語句:Show tables; Show partitions; describe查看錶結構。
Load語句:HIVE裝載數據時沒有做任何轉換,加載到表中的數據只是進入相應的配置單元表的位置。Load操作只是單純的複製/移動操作,將數據文件移動到Hive表對應的位置。
Insert語句:插入數據。Hive不支持一條一條的用 insert 語句進行插入操作,這個應該是與hive的storage layer是有關係的,因爲它的存儲層是HDFS,插入一個數據要全表掃描,還不如用整個表的替換來的快些。Hive也不支持update的操作。數據是以load的方式,加載到建立好的表中。數據一旦導入,則不可修改。要麼drop掉整個表,要麼建立新的表,導入新的數據。
Drop語句:刪除一個內部表的同時會同時刪除表的元數據和數據。刪除一個外部表,只刪除元數據而保留數據。
Limit子句:可以限制查詢的記錄數。查詢的結果是隨機選擇的。下面的查詢語句從 t1 表中隨機查詢5條記錄,SELECT * FROM t1 LIMIT 5。
Top K查詢:下面的查詢語句查詢銷售記錄最大的 5 個銷售代表。
SET mapred.reduce.tasks = 1
SELECT * FROM sales SORT BY amount DESC LIMIT 5
正則表達式使用:SELECT語句可以使用正則表達式做列選擇,下面的語句查詢除了ds和h 之外的所有列:
SELECT (ds|hr)?+.+ FROM sales
SELECT語句:查詢數據。
Group by, Order by, Sort by子句:聚合可進一步分爲多個表,甚至發送到 Hadoop 的 DFS 的文件(可以進行操作,然後使用HDFS的utilitites)。可以用hive.map.aggr控制怎麼進行彙總。默認爲爲true,配置單元會做的第一級聚合直接在MAP上的任務。這通常提供更好的效率,但可能需要更多的內存來運行成功。
Join語句:連接操作。一些注意事項:
(1)Hive只支持等值連接(equality joins)、外連接(outer joins)和(left/right joins)。Hive不支持所有非等值的連接,因爲非等值連接非常難轉化到map/reduce任務。
(2)Hive 支持多於2個表的連接。
(3)join 時,每次 map/reduce 任務的邏輯: reducer 會緩存 join 序列中除了最後一個表的所有表的記錄, 再通過最後一個表將結果序列化到文件系統。這一實現有助於在reduce端減少內存的使用量。實踐中,應該把最大的那個表寫在最後(否則會因爲緩存浪費大量內存)。
(4)LEFT,RIGHT 和 FULL OUTER 關鍵字用於處理 join 中空記錄的情況。
(5)LEFT SEMI JOIN 是 IN/EXISTS 子查詢的一種更高效的實現。Hive 當前沒有實現 IN/EXISTS 子查詢,所以你可以用 LEFT SEMI JOIN 重寫你的子查詢語句。LEFT SEMI JOIN的限制是, JOIN子句中右邊的表只能在ON子句中設置過濾條件,在WHERE子句、SELECT子句或其他地方過濾都不行。
6、Hive的擴展特性
Hive 是一個很開放的系統,很多內容都支持用戶定製,包括:
* 文件格式:Text File,Sequence File
* 內存中的數據格式: Java Integer/String, Hadoop IntWritable/Text
* 用戶提供的map/reduce腳本:不管什麼語言,利用stdin/stdout傳輸數據
* 用戶自定義函數:Substr, Trim, 1 – 1
* 用戶自定義聚合函數:Sum, Average…… n – 1
(1)數據文件格式
這裏寫圖片描述
例如使用文件文件格式存儲創建的表:

CREATE TABLE mylog ( user_id BIGINT, page_url STRING, unix_time INT)
STORED AS TEXTFILE;

當用戶的數據文件格式不能被當前Hive所識別的時候,可以自定義文件格式。可以參考contrib/src/java/org/apache/hadoop/hive/contrib/fileformat/base64中的例子。寫完自定義的格式後,在創建表的時候指定相應的文件格式就可以:

CREATE TABLE base64_test(col1 STRING, col2 STRING)
STORED AS
INPUTFORMAT 'org.apache.hadoop.hive.contrib.
fileformat.base64.Base64TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.contrib.
fileformat.base64.Base64TextOutputFormat';

(2)SerDe
SerDe是Serialize/Deserilize的簡稱,目的是用於序列化和反序列化。序列化的格式包括:分隔符(tab、逗號、CTRL-A)、Thrift 協議
反序列化(內存內):Java Integer/String/ArrayList/HashMap、Hadoop Writable類、用戶自定義類
其中,LazyObject只有在訪問到列的時候才進行反序列化。 BinarySortable保留了排序的二進制格式。
當存在以下情況時,可以考慮增加新的SerDe:
* 用戶的數據有特殊的序列化格式,當前的Hive不支持,而用戶又不想在將數據加載至Hive前轉換數據格式。
* 用戶有更有效的序列化磁盤數據的方法。
用戶如果想爲Text數據增加自定義Serde,可以參照contrib/src/java/org/apache/hadoop/hive/contrib/serde2/RegexSerDe.java中的例子。RegexSerDe利用用戶提供的正則表倒是來反序列化數據,例如:

CREATE TABLE apache_log(
host STRING,
identity STRING,
user STRING,
time STRING,
request STRING,
status STRING,
size STRING,
referer STRING,
agent STRING)
ROW FORMAT
SERDE 'org.apache.hadoop.hive.contrib.serde2.RegexSerDe'
WITH SERDEPROPERTIES
( "input.regex" = "([^ ]*) ([^ ]*) ([^ ]*) (-|\\[[^\\]]*\\])
([^ \"]*|\"[^\"]*\") (-|[0-9]*) (-|[0-9]*)(?: ([^ \"]*|\"[^\"]*\")
([^ \"]*|\"[^\"]*\"))?",
"output.format.string" = "%1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s";)
STORED AS TEXTFILE;

用戶如果想爲Binary數據增加自定義的SerDe,可以參考例子serde/src/java/org/apache/hadoop/hive/serde2/binarysortable,例如

CREATE TABLE mythrift_table
ROW FORMAT SERDE
'org.apache.hadoop.hive.contrib.serde2.thrift.ThriftSerDe'
WITH SERDEPROPERTIES (
"serialization.class" = "com.facebook.serde.tprofiles.full",
"serialization.format" = "com.facebook.thrift.protocol.TBinaryProtocol";);

(3)Map/Reduce腳本(Transform)
用戶可以自定義Hive使用的Map/Reduce腳本,比如:

FROM (
SELECT TRANSFORM(user_id, page_url, unix_time)
USING 'page_url_to_id.py'
AS (user_id, page_id, unix_time)
FROM mylog
DISTRIBUTE BY user_id
SORT BY user_id, unix_time)
mylog2
SELECT TRANSFORM(user_id, page_id, unix_time)
USING 'my_python_session_cutter.py' AS (user_id, session_info);

Map/Reduce腳本通過stdin/stdout進行數據的讀寫,調試信息輸出到stderr。
(4)UDF(User-Defined-Function)
用戶可以自定義函數對數據進行處理,例如:

<P>[sql]</P>
<P>add jar build/ql/test/test-udfs.jar;
CREATE TEMPORARY FUNCTION testlength
AS 'org.apache.hadoop.hive.ql.udf.UDFTestLength'; 

SELECT testlength(src.value) FROM src; 

DROP TEMPORARY FUNCTION testlength;</P>

UDFTestLength.java爲:

package org.apache.hadoop.hive.ql.udf; 

public class UDFTestLength extends UDF {
public Integer evaluate(String s) {
if (s == null) {
return null;
}
return s.length();
}
}

UDF 具有以下特性:
* 用java寫UDF很容易。
* Hadoop的Writables/Text 具有較高性能。
* UDF可以被重載。
* Hive支持隱式類型轉換。
* UDF支持變長的參數。
* genericUDF 提供了較好的性能(避免了反射)。
(5)UDAF(User-Defined Aggregation Funcation)
例子:
<P>[sql]</P>
<P>SELECT page_url, count(1), count(DISTINCT user_id) FROM mylog;</P>

UDAFCount.java代碼如下:

public class UDAFCount extends UDAF {
public static class Evaluator implements UDAFEvaluator {
private int mCount; 

public void init() {
mcount = 0;
} 

public boolean iterate(Object o) {
if (o!=null)
mCount++; 

return true;
} 

public Integer terminatePartial() {
return mCount;
} 

public boolean merge(Integer o) {
mCount += o;
return true;
} 

public Integer terminate() {
return mCount;
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章