Hive中 分區表和桶
Hive分區表
在hive Select 查詢中一般會掃描整個表內容,會消耗很多時間做沒必要的工作.有時候隻需要掃描表中關心的一部分數據,因此建表時引入了partition概念.分許表指的是在創建表時指定的partition的分區空間.
Hive可以對數據按照某列或者某些列進行分區管理,所謂分區我們可以拿下麵的列子進行解釋.
當前互聯網應用每天都要存儲大量的日誌文件.幾G.十幾G甚至更大都是有可能的.存儲日誌,其中必然有個屬性是日誌產生的日期.在產生分區時,就可以按照日誌產生的日期列進行劃分.把每一天的日誌當做一個分區.
將數據組織成分區,主要可以提高數據的查詢速度.至於用戶存儲的每一天記錄到底放到那個分區,由用戶決定.即用戶在加載數據的時候必須顯示製定該部分數據放到那個分區.
實現細節
一個表可以擁有一個或者多個分區 每個分區一文件夾的形式單獨存儲在表文件夾的目錄下.
表和列名不區分大小寫
分區是以字段的形式在表結構中存在.通過describe table 命令可以查看到字段存在.
語法
創建一個分區表 以ds 為分區列
create table invites (id int, name string) partitioned by (ds string) row format
delimited fields terminated by 't' stored as textfile;
將數據添加到時間為 2017-10-25 這個分區中:
load data local inpath '/home/hadoop/data.txt' overwrite into table invites partition(ds='2017-10-25');
將數據添加到時間為2017-10-25這個分區中
load data local inpath '/home/hadoop/data.txt/' overwrite into table invites partition(ds='2017-10-25')
從一個分區中查詢數據
select * from invites where ds='2017-10-25'
往一個分區表的某一個分區中添加數據:
insert into overwrite table invites partition (ds='2017-10-25') select id ,max(name) from test group by id;
可以查看分區的具體情況,使用命令:
hadoop fs -ls /home/hadoop/warehouse/invites
或者 show partitions tablename;
Hive 桶
對於每一個表或者分區 Hive可以進一步組織成桶 ,也就是說桶是更為細粒度的數據範圍劃分.Hive也是針對某一列進行桶的組織.Hive采用對列值哈希,然後除以桶的個數求餘的方式決定條記錄存放在哪個桶當中.
把表組織成桶的有兩個理由:
1>獲得更高的查詢處理效率.桶為表加上了額外的結構,Hive在處理某些查詢時能夠利用這個結構.具體而言,連接兩個在相同列上劃分痛的表,可以使用map操作兩個表有一個相同的列.如果對這兩個表都進行了桶操作.那麼將保存相同列值的桶進行JOIN操作 就可以,可以大大減少JOIN的數據量.
2>使用樣sampling 更高效 .在處理大規模數據集時,在開發和修改查詢的階段,如果能在數據集的一小部分數據上試運行查詢,會帶來很多方便.
- 創建帶桶的 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; 首先,我們來看如何告訴Hive—個表應該被劃分成桶。我們使用CLUSTERED BY 子句來指定劃分桶所用的列和要劃分的桶的個數:
CREATE TABLE bucketed_user (id INT) name STRING)
CLUSTERED BY (id) INTO 4 BUCKETS;
在這裏,我們使用用戶ID來確定如何劃分桶(Hive使用對值進行哈希並將結果除 以桶的個數取餘數。這樣,任何一桶裏都會有一個隨機的用戶集合(PS:其實也能說是隨機,不是嗎?)。
對於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
強製多個 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'往表中插入數據:
INSERT OVERWRITE TABLE bucketed_users SELECT * FROM users;
物理上,每個桶就是表(或分區)目錄裏的一個文件。它的文件名並不重要,但是桶 n 是按照字典序排列的第 n 個文件。事實上,桶對應於 MapReduce 的輸出文件分區:一個作業產生的桶(輸出文件)和reduce任務個數相同。我們可以通過查看剛才 創建的bucketd_users表的布局來了解這一情況。運行如下命令:
查看表的結構:
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
第一個桶裏包括用戶IDO和4,因為一個INT的哈希值就是這個整數本身,在這裏 除以桶數(4)以後的餘數:②讀取數據,看每一個文件的數據:
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。
②顯式原始文件時,因為分隔字符是一個不能打印的控製字符,因此字段都擠在一起。
3、舉個完整的小例子:
(1)建student & student1 表:
1 create table student(id INT, age INT, name STRING)
2 partitioned by(stat_date STRING)
3 clustered by(id) sorted by(age) into 2 buckets
4 row format delimited fields terminated by ',';
5
6 create table student1(id INT, age INT, name STRING)
7 partitioned by(stat_date STRING)
8 clustered by(id) sorted by(age) into 2 buckets
9 row format delimited fields terminated by ',';
(2)設置環境變量:
set hive.enforce.bucketing = true;
(3)插入數據:
01 cat bucket.txt
02
03 1,20,zxm
04 2,21,ljz
05 3,19,cds
06 4,18,mac
07 5,22,android
08 6,23,symbian
09 7,25,wp
10
11 LOAD DATA local INPATH '/home/lijun/bucket.txt' OVERWRITE INTO TABLE student partition(stat_date="20120802");
12
13 from student
14 insert overwrite table student1 partition(stat_date="20120802")
15 select id,age,name where stat_date="20120802" sort by age;
(4)查看文件目錄:
hadoop fs -ls /hive/warehouse/test.db/student1/stat_date=20120802
Found 2 items
-rw-r--r-- 2 lijun supergroup 31 2013-11-24 19:16 /hive/warehouse/test.db/student1/stat_date=20120802/000000_0
-rw-r--r-- 2 lijun supergroup 39 2013-11-24 19:16 /hive/warehouse/test.db/student1/stat_date=20120802/000001_0
(5)查看sampling數據:
hive> select * from student1 tablesample(bucket 1 out of 2 on id);
Total MapReduce jobs = 1
Launching Job 1 out of 1
.......
OK
4 18 mac 20120802
2 21 ljz 20120802
6 23 symbian 20120802
Time taken: 20.608 seconds
注:tablesample是抽樣語句,語法:TABLESAMPLE(BUCKET x OUT OF y)
y必須是table總bucket數的倍數或者因子。hive根據y的大小,決定抽樣的比例。例如,table總共分了64份,當y=32時,抽取(64/32=)2個bucket的數據,當y=128時,抽取(64/128=)1/2個bucket的數據。x表示從哪個bucket開始抽取。例如,table總bucket數為32,tablesample(bucket 3 out of 16),表示總共抽取(32/16=)2個bucket的數據,分別為第3個bucket和第(3+16=)19個bucket的數據。
最後更新:2017-10-25 22:04:08