Hive中 分区表和桶

来源:互联网 发布:打印机端口怎么看 编辑:程序博客网 时间:2024/05/31 19:55

点击有惊喜


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 更高效 .在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便.

  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;  首先,我们来看如何告诉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 

  1. 强制多个 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'  

  2. 往表中插入数据:
    INSERT OVERWRITE TABLE bucketed_users SELECT * FROM users; 

物理上,每个桶就是表(或分区)目录里的一个文件。它的文件名并不重要,但是桶 n 是按照字典序排列的第 n 个文件。事实上,桶对应于 MapReduce 的输出文件分区:一个作业产生的桶(输出文件)和reduce任务个数相同。我们可以通过查看刚才 创建的bucketd_users表的布局来了解这一情况。运行如下命令:  

  1. 查看表的结构:
    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)以后的余数:② 

  2. 读取数据,看每一个文件的数据:
    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的数据。


点击有惊喜


原创粉丝点击