Hive分桶(bucket)
一 什么是桶的概念,和分区有啥区别?
对于每一个表或者分区,可以进一步细分成桶,桶是对数据进行更细粒度的划分。默认时对某一列进行hash,使用hashcode对 桶的个数求模取余,确定哪一条记录进入哪一个桶。
Hive在查询数据的时候,一般会扫描整个表的数据,会消耗很多不必要的时间。有些时候,我们只需要关心一部分数据,比如WHERE子句所接的查询条件,那这时候这种全表扫描的方式是很影响性能的。从而引入了分区的概念。分区就是对某列有相同的数据或者某一个数据范围的数据进行分类,这样在查询的时候,就可以只是针对分区查询,从而不必全表扫描。
二 如何分桶? 如何导入数据?
2.1导入数据
CREATE TABLE IF NOT EXISTS t_movie(
idINT,
nameSTRING,
directorSTRING,
countrySTRING,
yearSTRING,
monthSTRING
)ROWFORMAT DELIMITED FIELDS TERMINATED BY ',';
COMMENT'Create Bucket Movie Table'
PARTITIONEDBY (area STRING)
CLUSTEREDBY (country) INTO 4 BUCKETS
ROWFORMAT DELIMITED FIELDS TERMINATED BY ','
STOREDAS ORC;
CLUSTEREDBY: 指定根据哪一列来划分桶
INTOnum BUCKTES: 指定划分几个桶
2.2导入数据
我们需要设置sethive.enforce.bucketing=true;
如果我们没有设置hive.enfoce.bucketing这个参数,那么我们需要设置和分桶个数相匹配的Reducer数目,set mapred.reduce.tasks=4,并且查询的时候需要添加CLSUTERBY子句。
所以如果设置了我们查询的时候就不必设置Reducer数目,和查询的时候不必指定CLSUTRER BY子句。
INSERT INTO TABLE bucket_movie PARTITION(area='China') SELECT * FROM t_movie WHERE country = 'China';
INSERT INTO TABLE bucket_movie PARTITION(area='America') SELECT * FROM t_movie WHERE country = 'America';
INSERT INTO TABLE bucket_movie PARTITION(area='Japan') SELECT * FROM t_movie WHERE country = 'Japan';
三 分桶作用
3.1 获得更高的查询效率
我们试想一个场景:比如2张大表需要JOIN,JOIN的字段比如是id,我们进行Reduce Side Join(ShuffleJoin)合适吗?肯定不合适。
如果我们用Map Side Join呢?Map Side Join场景是小表Join大表比较适合,因为会把小表数据是通过DistributedCache 分发到各个Map
Side,然后加载到内存和每一个Map 任务处理的大表进行JOIN,这样就不必要去做Reduce JOIN, 但是如果是大表就不太适合放到内存去了。
所以Bucket这时候在Map SideJoin就有勇武之地了。
原理:
2张表对于连接的字段进行分桶,处理左边表内某个桶的Mapper他知道右边表内对应的行在对应的桶内,因此Mapper只需要获取那个桶,然后取得数据进行JOIN
如图示:
我们需要将用户表和订单表进行join。
如果我们没有分桶,那么这2张大表在JOIN的时候,效率是很低的。
那现在我们引入桶的概念,那么用户表按照id分桶,订单表按照
cid分桶,那么相同id都会归入一个桶。那么此时再进行JOIN的时候是按照桶来JOIN的,那么大大减少了JOIN的数量。
但是这是需要一定条件的,否则JOIN出来的结果是不正确的:
用户表的桶的个数必须是订单表的倍数或者因子,也就是说订单表100各个桶,那么用户表可以是200,300或者10,20,25,50
为什么要这么做呢?
我们知道分桶会对该列进行hash,然后根据桶的数量取余计算每一个记录落在哪一个桶。
比如A表4个桶,B表5个桶,那么假设hash取余之后的数A表肯定是0,1,2,3 那么B表的话则是0,1,2,3,4
问题来呢?如果假设hashcode得到的值为8,那么在A表,这条数据就落在0这个桶内,而在表则会落在3这个桶内.
如果要想Hive执行BucketMap Join,我们需要确保这个参数是否为true:
hive.optimize.bucketmapjoin= true
step1: 先将小表做map,数据放入hashtable,广播到所有大表的Map端,大表map端接受了小表的hashtable,并不需要合并成一个大的hashtable,直接可以进行map操作,map操作会产生桶个数的Split,然后小表数据放入内存,然后大表对应的split拿出来判断。但是这时候还是有可能内存不够用,所以并没有完全解决Map Side Join在小表完全装在进内存的限制。
如果桶中的数据可以根据一个或多个列另外进行排序。由于这样对每个桶的连接变成了高效的归并排序(merge-sort), 因此可以进一步提升map端连接的效率。以下语法声明一个表使其使用排序桶:
CREATE TABLE table_name (col_name data_type,……)
CLUSTERED BY (col_name) SORTED BY(col_name ASC|DESC)
set hive.optimize.bucketmapjoin.sortedmerge = true;
sethive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHive
InputFormat;
3.2 方便我们抽样
我们知道抽象:
SELECT * FROM table_name TABLESAMPLE(nPERCENT);
就是针对n%进行取样
有了桶之后呢?
SELECT * FROM film TABLESAMPLE(BUCKET x OUTOF y)
x:表示从哪一个桶开始抽样
y:抽样因素,必须是桶数的因子或者倍数,假设桶数是100那么y可以是200,10,20,25,5。假设桶数是100,y=25时抽取(100/25) = 4个bucket数据;当y=200的时候(100/200) = 0.5个bucket的数据