整套大数据学习资料(视频+笔记)百度网盘无门槛下载:http://www.edu360.cn/news/content?id=3377

12.6.2 分区和桶

hadoop 小红牛 10℃ 0评论

Hive把表组织成“分区”(partition)。这是一种根据“分区列”(partition column,如日期)的值对表进行粗略划分的机制。使用分区可以加快数据 分片(slice)的査询速度。

表或分区可以进一步分为“桶”(bucket)。它会为数据提供额外的结构以获 得更高效的查询处理。例如,通过根据用户ID来划分桶,我们可以在所有 用户集合的随机样本上快速计算基于用户的查询。

1.分区

以分区的常用情况为例。考虑日志文件,其中每条记录包含一个时间戳。 如果我们根据日期来对它进行分区,那么同一天的记录就会被存放在同一 个分区中。这样做的优点是:对于限制到某个或某些特定日期的査询,它 们的处理可以变得非常高效。因为它们只需要扫描査询范围内分区中的文

件。注意,使用分区并不会影响大范围查询的执行:我们仍然可以查询跨 多个分区的整个数据集。

一个表可以以多个维度来进行分区。例如,在根据日期对日志进行分区以 外,我们可能还要进一步根据国家对每个分区进行子分区(subpartition),以 加速根据地理位置进行的査询。 分区是在创建表的时候用PARTITIONED BY子句定义的®。该子句需要定义 列的列表。例如,对前面提到的假想的日志文件,我们可能要把表记录定 义为由时间戳和日志行构成:

CREATE TABLE logs (ts BIGINT, line STRING)
PARTITIONED BY (dt STRING, country STRING);

在我们把数据加载到分区表的时候,要显式指定分区值:

LOAD DATA LOCAL INPATH ’input/hive/partitions/filel'
INTO TABLE logs
PARTITION (dt=’2001-01-01’,, country’GB’);

在文件系统级別,分区只是表目录下嵌套的子目录。把更多文件加载到 logs表以后,目录结构可能像下面这样:

/user/hive/warehouse/logs

图片.png

日志表有两个日期分区:2001-01-01和2001-01-02,分别对应于子目录 dt=2001-01-01和出=2001-01-02;和两个国家分区:GB和US,分别对应 于嵌套子目录country=GB和country=US。数据文件则存放在底层目录中。

可以用SHOW PARTITIONS命令让Hive告诉我们表中有哪些分区:

hive> SHOW PARTITIONS logs;

dt=2001-01-01/country=GB

dt=2001-01-01/country=US

dt=2001-01-02/country=GB

dt=2001-01-02/country=US

记住,PARTITIONED BY子句中的列定义是表中正式的列,称为“分区 列”(partition column),但是,数据文件并不包含这些列的值,因为它们源于目录名。

可以在SELECT语句中以通常的方式使用分区列。Hive会对输入进行修剪,从而只扫描相关的分区。例如:

SELECT ts, dt, line
FROM logs ,
WHERE country='GB';

将只扫描filel、file2和file4。还要注意,这个査询返回dt分区列的 值。这个值是Hive从目录名中读取的,因为它们在数据文件中并不存在。

2.桶

把表(或分区)组织成桶(bucket)有两个理由。第一个理由是获得更高的査询
处理效率。桶为表加上了额外的结构。Hive在处理有些查询时能够利用这 个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可
以使用map端连接(map-side join)高效地实现。

把表划分成桶的第二个理由是使“取样”(sampling)更高效。在处理大规模 数据集时,在开发和修改査询的阶段,如果能在数据集的一小部分数据上 试运行查询,会带来很多方便。我们在本节的最后将看看如何高效地进行 取样。

首先,我们来看如何告诉Hive 一个表应该被划分成桶。我们使用 CLUSTERED BY子句来指定划分桶所用的列和要划分的桶的个数:

CREATE TABLE bucketed_users (id INT, name STRING)
CLUSTERED BY (id) INTO 4 BUCKETS;

在这里,我们使用用户ID来确定如何划分桶(Hive对值进行哈希并将结果 除以桶的个数取余数。这样,任何一桶里都会有一个随机的用户集合。

对于map端连接的情况,首先两个表以相同方式划分桶,处理左边表内某

个桶的mapper知道右边表内相匹配的行在对应的桶内,这样,mapper只需要获取那个桶(这只是右边表内存储数据的一小部分)即可进行连接。这一优
化方法并不一定要求两个表必须具有相同的桶的个数,两个表的桶个数是 倍数关系也可以。用HiveQL对两个划分了桶的表进行连接,更多细节可参
见12.7.3节对map连接的讨论。

桶中的数据可以根据一个或多个列另外进行排序。由于这样对每个桶的连 接变成了高效的归并排序(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 Joe
3 Kay
4 Ann

要向分桶后的表中填充成员,需要将hive.enforce.bucketing属性设置 为true。这样,Hive就知道用表定义中声明的数量来创建桶,然后使用 INSERT命令即可:

INSERT OVERWRITE TABLE bucketed_users 
SELECT * FROM users;

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

hive> dfs -Is /user/hive/warehouse/bucketed__usens;

将显示有4个新建的文件。文件名如下(文件名由Hive产生):

000000_0

000001_0

000002_0

000003_0

第一个桶里包括用户ID 0和4,因为一个INT的哈希值就是这个整数本 身,在这里即除以桶数(4)以后的余数:®

hive> dfs -cat /user/hive/warehouse/bucketed_users/000000_0;
0Nat
4Ann

用TABLESAMPLE子句对表进行取样,我们可以获得相同的结果。这个子 句会将查询限定在表的一部分桶内,而不是使用整个表:

hive〉SELECT * FROM bucketed__users
> TABLESAMPLE(BUCKET 1 OUT OF 4 ON id);
0 Nat
4 Ann

桶的个数从1开始计数。因此,前面的査询从4个桶的第一个中获取所有
的用户。对于一个大规模的、均匀分布的数据集,这会返回表中约1/4的数 据行。我们也可以用其他比例对若干个桶进行取样(因为取样并不是一个精
确的操作,因此这个比例不一定是桶数的整数倍)。例如,下面的査询返回一半 的桶:

hive> SELECT * FROM bucketed_users
> TABLESAMPLE(BUCKET 1 OUT OF 2 ON id);
0 Nat 
4 Ann
2 3oe

因为査询只需要读取和TABLESAMPLE子句匹配的桶,所以取样分桶表是非 常高效的操作。如果使用rand()函数对没有划分成桶的表进行取样,即使 只需要读取很小一部分样本,也要扫描整个输入数据集:

hive> SELECT * FROM users
> TABLESAHPLE(BUCKET 1 OUT OF 4 ON rand());
2 Joe

转载请注明:全栈大数据 » 12.6.2 分区和桶

喜欢 (0)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址