(本文是基於多篇文章根據個人理解進行的整合,參考的文章見末尾的整理)
數據模型
hive的數據模型包括:database、table、partition和bucket。
1.Database:相當於關系數據庫里的命名空間(namespace),它的作用是將用戶和數據庫的應用隔離到不同的數據庫或模式中,該模型在hive 0.6.0之后的版本支持,hive提供了create database dbname、use dbname以及drop database dbname這樣的語句。
2.表(table):hive的表邏輯上由存儲的數據和描述表格中的數據形式的相關元數據組成。元數據存儲在關系數據庫里。表存儲的數據存放在hive的數據倉庫中,這個數據倉庫也就是hdfs上的一個目錄,該目錄是在hive-site.xml中由${hive.metastore.warehouse.dir}指定的,這里假定為/user/hive/warehouse/。創建一張hive的表,就是在hdfs的倉庫目錄下創建一個文件夾。表分為內部表和外部表兩種。
2.1元數據
Hive將元數據存儲在RDBMS中,一般常用的有MYSQL和DERBY。
Apache Derby非常小巧,核心部分derby.jar只有2M,所以既可以做為單獨的數據庫服務器使用,也可以內嵌在應用程序中使用。所以hive采用了Derby作為一個內嵌的元數據庫,可以完成hive安裝的簡單測試。 hive安裝完成之后,就可以在hive shell中執行一些基本的操作,創建表、查詢等等。如果你細心的話,就會發現一個問題:當在某個目錄下啟動終端,進入hive shell時,hive默認會在當前目錄下生成一個derby文件和一個metastore_db目錄,這兩個文件主要保存剛剛在shell中操作的一些sql的結果,比如新建的表、添加的分區等等。
這種方式的弊端 :1.在同一個目錄下同時只能有一個hive客戶端能使用數據庫;2.切換目錄啟動新的shell,無法查看之前創建的表,不能實現表數據的共享。由於這些弊端,所以采用mysql保存hive元數據解決上面的問題。hive所有的元數據都保存在同一個庫里,這樣不同開發者創建的表可以實現共享。
hive元數據對應的表約有20個,其中和表結構信息有關的有9張,其余的10多張或為空,或只有簡單的幾條記錄,以下是部分主要表的簡要說明。
表名 |
說明 |
關聯鍵 |
TBLS |
所有hive表的基本信息 |
TBL_ID,SD_ID |
TABLE_PARAM |
表級屬性,如是否外部表,表注釋等 |
TBL_ID |
COLUMNS |
Hive表字段信息(字段注釋,字段名,字段類型,字段序號) |
SD_ID |
SDS |
所有hive表、表分區所對應的hdfs數據目錄和數據格式 |
SD_ID,SERDE_ID |
SERDE_PARAM |
序列化反序列化信息,如行分隔符、列分隔符、NULL的表示字符等 |
SERDE_ID |
PARTITIONS |
Hive表分區信息 |
PART_ID,SD_ID,TBL_ID |
PARTITION_KEYS |
Hive分區表分區鍵 |
TBL_ID |
PARTITION_KEY_VALS |
Hive表分區名(鍵值) |
PART_ID |
從上面表的內容來看,hive整個創建表的過程已經比較清楚了。
(1)解析用戶提交hive語句,對其進行解析,分解為表、字段、分區等hive對象
(2)根據解析到的信息構建對應的表、字段、分區等對象,從SEQUENCE_TABLE中獲取構建對象的最新ID,與構建對象信息(名稱,類型等)一同通過DAO方法寫入到元數據表中去,成功后將SEQUENCE_TABLE中對應的最新ID+5。
實際上我們常見的RDBMS都是通過這種方法進行組織的,典型的如postgresql,其系統表中和hive元數據一樣裸露了這些id信息(oid,cid等),而Oracle等商業化的系統則隱藏了這些具體的ID。通過這些元數據我們可以很容易的讀到數據諸如創建一個表的數據字典信息,比如導出建表語名等。
2.2 內部表
內部表數據文件存儲在hive的數據倉庫里。刪除表時,元數據與數據都會被刪除。
簡單實例:
1 在本地創建數據文件:/home/test_inner_data.txt
2 創建表:Create table test_inner_table (key string);
注:在/user/hive/warehouse/目錄下創建一個名為 test_inner_table的文件夾
3 加載數據:Load local inpath '/home/test_inner_data.txt' into table test_inner_table;
注:將/home/test_inner_data.txt→ 復制到hdfs:/home/hdfs/test_inner_data.txt(Hive中進行配置,臨時保存)→移動到hdfs:/user/hive/warehouse/test_inner_table/test_inner_data.txt。hive加載數據時候不會對元數據進行任何檢查,只是簡單的移動文件的位置,如果源文件格式不正確,也只有在做查詢操作時候才能發現,那個時候錯誤格式的字段會以NULL來顯示。
4 查看數據:Select * from test_inner_table;
5 刪除表:Drop table test_inner_table
注:test_inner_table文件夾以及包含的所有數據被移到hdfs:/user/hdfs/.Trash/Current文件夾中(如果你的Hadoop沒有取用垃圾箱機制,那么drop table 命令將會把其刪除!)
2.3 外部表
外部表的數據文件可以存放在hive數據倉庫外部的分布式文件系統上,也可以放到hive數據倉庫里。外部表實際數據是存儲在LOCATION后面指定的HDFS路徑中,若不指定則移動到數據倉庫目錄中。當刪除一個ExternalTable時,僅刪除該元數據。
簡單示例:
1 在本地創建數據文件:/home/test_external_data.txt
2 創建表:Create external table test_external_table (key string) Location '/home/hadoop/external';
注:不創建任何文件夾。若不指定location,則默認在hdfs://user/hive/warehouse/下創建一個test_external_table文件夾;
3 加載數據:Load data local inpath '/home/test_external_data.txt' into table test_external_table;
注:將/home/test_external_data.txt→ 復制到hdfs:/home/hdfs/test_external_data.txt→移動到hdfs:/home/hadoop/external/test_external_data.txt。若不指定location,將/home/test_external_data.txt → 復制到hdfs:/home/hdfs/test_external_data.txt → 移動到hdfs:/user/hive/warehouse/test_external_table/test_external_data.txt
4 查看數據:select * from test_external_table;
5 刪除表:drop table test_external_table
注:hdfs:/home/hadoop/external/test_external_data.txt並沒有被刪除。若不指定location,待定
3.分區(partition)
hive里分區的概念是根據“分區列”的值對表的數據進行粗略划分的機制, 在hive存儲上就體現在表的主目錄下的一個子目錄,這個文件夾的名字就是我們定義的“分區列+值”。
分區以字段的形式存在表結構中,通過describe table命令可以查看到字段存在,但並不是對應着數據文件中某個列的字段,它不存放實際的數據內容,僅僅是分區的表示(偽列)。
至於用戶存儲的每個數據文檔到底放到哪個分區,由用戶決定,只是單純的數據文檔的移動。即用戶在加載數據的時候必須顯示的指定該部分數據放到哪個分區。
進行分區的好處:(1)提高查詢效率。在Hive Select查詢中一般會掃描整個表內容,會消耗很多時間做沒必要的工作。有時候只需要掃描表中關心的一部分數據,因此建表時引入了partition概念。如:當前互聯網應用每天都要存儲大量的日志文件,幾G、幾十G甚至更大都是有可能。存儲日志,其中必然有個屬性是日志產生的日期。在產生分區時,就可以按照日志產生的日期列進行划分。把每一天的日志當作一個分區。
具體例子如下:
1. 創建一個分區表,以 ds 為分區列:
create table invites (id int, name string)
partitioned by (ds string)
row format delimited
fields terminated by 't' stored as textfile;
2. 將數據添加到時間為 2013-08-16 這個分區中:
load data local inpath '/home/hadoop/Desktop/data.txt' overwrite into table invites partition (ds='2013-08-16');
3. 從一個分區中查詢數據:
select * from invites where ds ='2013-08-16';
4. 往一個分區表的某一個分區中添加數據:
insert overwrite table invites
partition (ds='2013-08-16')
select id,max(name) from test group by id;
5. 可以查看分區的具體情況,使用命令:
hadoop fs -ls /home/hadoop.hive/warehouse/invites 或者 show partitions tablename;
4. Hive 桶
對於每一個表(table)或者分區, Hive可以進一步組織成桶,也就是說桶是更為細粒度的數據范圍划分。
它是對數據源數據文件本身來拆分數據,使用桶的表會將源數據文件按一定規律拆分成多個文件。物理上,每個桶就是表(或分區)目錄里的一個文件。
Hive也是針對某一列進行桶的組織,這里的列字段是對應於數據文件中具體某個列的。
Hive采用對列值哈希,然后除以桶的個數求余的方式決定該條記錄存放在哪個桶當中。
把表(或者分區)組織成桶(Bucket)好處:
(1)獲得更高的查詢處理效率。桶為表加上了額外的結構,Hive 在處理有些查詢時能利用這個結構。具體而言,連接兩個在(包含連接列的)相同列上划分了桶的表,可以使用 Map 端連接 (Map-side join)高效的實現。比如JOIN操作。對於JOIN操作兩個表有一個相同的列,如果對這兩個表都進行了桶操作。那么將保存相同列值的桶進行JOIN操作就可以,可以大大較少JOIN的數據量。
(2)使取樣(sampling)更高效。在處理大規模數據集時,在開發和修改查詢的階段,如果能在數據集的一小部分數據上試運行查詢,會帶來很多方便。
具體例子如下:
1. 創建帶桶的 table :
create table bucketed_user(id int,name string)
clustered by (id) sorted by(name) into 4 buckets
row format delimited fields terminated by '\t' stored as textfile;
使用CLUSTERED BY 子句來指定划分桶所用的列和要划分的桶的個數。
對於map端連接的情況,兩個表以相同方式划分桶。處理左邊表內某個桶的 mapper知道右邊表內相匹配的行在對應的桶內。因此,mapper只需要獲取那個桶 (這只是右邊表內存儲數據的一小部分)即可進行連接。這一優化方法並不一定要求 兩個表必須桶的個數相同,兩個表的桶個數是倍數關系也可以。用HiveQL對兩個划分了桶的表進行連接,可參見“map連接”部分(P400)。
桶中的數據可以根據一個或多個列另外進行排序。由於這樣對每個桶的連接變成了高效的歸並排序(merge-sort), 因此可以進一步提升map端連接的效率。以下語法聲明一個表使其使用排序桶:
CREATE TABLE bucketed_users (id INT, name STRING)
CLUSTERED BY (id) SORTED BY (id ASC) INTO 4 BUCKETS;
我們如何保證表中的數據都划分成桶了呢?把在Hive外生成的數據加載到划分成 桶的表中,當然是可以的。其實讓Hive來划分桶更容易。這一操作通常針對已有的表。
Hive並不檢查數據文件中的桶是否和表定義中的桶一致(無論是對於桶 的數量或用於划分桶的列)。如果兩者不匹配,在査詢時可能會碰到錯 誤或未定義的結果。因此,建議讓Hive來進行划分桶的操作。
有一個沒有划分桶的用戶表:
hive> SELECT * FROM users;
0 Nat
2 Doe
B Kay
4 Ann
2. 強制多個 reduce 進行輸出:
要向分桶表中填充成員,需要將 hive.enforce.bucketing 屬性設置為 true。①這 樣,Hive 就知道用表定義中聲明的數量來創建桶。然后使用 INSERT 命令即可。
需要注意的是: clustered by和sorted by不會影響數據的導入,這意味着,用戶必須自己負責數據如何如何導入,包括數據的分桶和排序。
'set hive.enforce.bucketing = true' 可以自動控制上一輪reduce的數量從而適配bucket的個數,當然,用戶也可以自主設置mapred.reduce.tasks去適配bucket個數,推薦使用'set hive.enforce.bucketing = true'
3. 往表中插入數據:
INSERT OVERWRITE TABLE bucketed_users SELECT * FROM users;
物理上,每個桶就是表(或分區)目錄里的一個文件。它的文件名並不重要,但是桶 n 是按照字典序排列的第 n 個文件。事實上,桶對應於 MapReduce 的輸出文件分區:一個作業產生的桶(輸出文件)和reduce任務個數相同。我們可以通過查看剛才 創建的bucketd_users表的布局來了解這一情況。運行如下命令:
4. 查看表的結構:
hive> dfs -ls /user/hive/warehouse/bucketed_users;
將顯示有4個新建的文件。文件名如下(文件名包含時間戳,由Hive產生,因此 每次運行都會改變):
attempt_201005221636_0016_r_000000_0
attempt_201005221636_0016_r-000001_0
attempt_201005221636_0016_r_000002_0
attempt_201005221636_0016_r_000003_0
第一個桶里包括用戶ID 0和4,因為一個INT的哈希值就是這個整數本身,在這里 除以桶數(4)以后的余數:②
5. 讀取數據,看每一個文件的數據:
hive> dfs -cat /user/hive/warehouse/bucketed_users/*0_0;
0 Nat
4 Ann
用TABLESAMPLE子句對表進行取樣,我們可以獲得相同的結果。這個子句會將查詢限定在表的一部分桶內,而不是使用整個表:
6. 對桶中的數據進行采樣:
hive> SELECT * FROM bucketed_users
> TABLESAMPLE(BUCKET 1 OUT OF 4 ON id);
0 Nat
4 Ann
桶的個數從1開始計數。因此,前面的查詢從4個桶的第一個中獲取所有的用戶。 對於一個大規模的、均勻分布的數據集,這會返回表中約四分之一的數據行。我們 也可以用其他比例對若干個桶進行取樣(因為取樣並不是一個精確的操作,因此這個 比例不一定要是桶數的整數倍)。
例如,下面的查詢返回一半的桶:
7. 查詢一半返回的桶數:
hive> SELECT * FROM bucketed_users
> TABLESAMPLE(BUCKET 1 OUT OF 2 ON id);
0 Nat
4 Ann
2 Joe
因為查詢只需要讀取和TABLESAMPLE子句匹配的桶,所以取樣分桶表是非常高效 的操作。如果使用rand()函數對沒有划分成桶的表進行取樣,即使只需要讀取很 小一部分樣本,也要掃描整個輸入數據集:
hive〉 SELECT * FROM users
> TABLESAMPLE(BUCKET 1 OUT OF 4 ON rand());
2 Doe
①從Hive 0.6.0開始,對以前的版本,必須把mapred.reduce .tasks設為表中要填 充的桶的個數。如果桶是排序的,還需要把hive.enforce.sorting設為true。
②顯式原始文件時,因為分隔字符是一個不能打印的控制字符,因此字段都擠在一起。
參考:
Hive介紹 http://www.cnblogs.com/sharpxiajun/archive/2013/06/02/3114180.html
Hive的數據類型和數據模型 http://www.cnblogs.com/sharpxiajun/archive/2013/06/03/3114560.html
Hive內表與外表詳述 http://www.aboutyun.com/thread-7458-1-1.html
Hive基礎之分區和桶 http://my.oschina.net/leejun2005/blog/178631
Hive的體系結構 http://blog.csdn.net/zhoudaxia/article/details/8855937
Hive的元數據庫配置,metadata相關 http://www.2cto.com/database/201411/352706.html
Hadoop Hive SQL語法詳解 http://blog.csdn.net/hguisu/article/details/7256833
Hive幾種數據導入方式 http://www.iteblog.com/archives/94
Hive專欄 http://www.iteblog.com/archives/category/hive
Hive專欄 http://my.oschina.net/leejun2005/blog?catalog=384549
Hive資料集錦 http://my.oschina.net/leejun2005/blog/140462#OSC_h3_2
Programing Hive http://flyingdutchman.iteye.com/category/277341
Hive官網 https://cwiki.apache.org/confluence/display/Hive/GettingStarted