Hive 表的优化

文章目录

  • Join
  • MapJoin

Join

Join原则:

  • 1)小表Join大表
    key相对分散,并且数据量小的表放在join的左边,这样可以有效减少内存溢出错误发生的几率;再进一步,可以使用Group让小的维度表(1000条以下的记录条数)先进内存。在map端完成reduce
    select count(distinct s_id) from score;
    select count(s_id) from score group by s_id;map端进行聚合,效率更高

  • 2)多个表关联时,最好分拆成小段,避免大sql(无法控制中间Job)

  • 3)大表Join大表
    (1)空KEY过滤
    有时join超时是因为某些key对应的数据太多,而相同key对应的数据都会发送到相同的reducer上,从而导致内存不够。此时我们应该仔细分析这些异常的key,很多情况下,这些key对应的数据是异常数据,我们需要在SQL语句中进行过滤。例如key对应的字段为空,操作如下:
    环境准备:

创建表
create table ori(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';
创建表create table nullidtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';创建表
create table jointable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';导入数据
load data local inpath '/export/servers/hivedatas/hive_big_table/*' into table ori; 导入数据
load data local inpath '/export/servers/hivedatas/hive_have_null_id/*' into table nullidtable;

不过滤:

插入数据
INSERT OVERWRITE TABLE jointable查询数据
SELECT a.* FROM nullidtable a JOIN ori b ON a.id = b.id;
结果:
No rows affected (152.135 seconds)

过滤:

插入数据
INSERT OVERWRITE TABLE jointable查询数据
SELECT a.* FROM (SELECT * FROM nullidtable WHERE id IS NOT NULL ) a JOIN ori b ON a.id = b.id;
结果:
No rows affected (141.585 seconds)

(2)空key转换
有时虽然某个key为空对应的数据很多,但是相应的数据 不是异常数据,必须要包含在join的结果中,此时我们可以表a中key为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的reducer上。例如:
不随机分布:

设置
set hive.exec.reducers.bytes.per.reducer=32123456;set mapreduce.job.reduces=7;
插入数据
INSERT OVERWRITE TABLE jointable查询数据
SELECT a.* FROM nullidtable a LEFT JOIN ori b ON  CASE WHEN  a.id IS NULL  THEN 'hive'  ELSE a.id  END  = b.id;
No rows affected (41.668 seconds)   52.477

结果:这样的后果就是所有为null值的id全部都变成了相同的字符串“hive”,及其容易造成数据的倾斜(所有的key相同,相同key的数据会到同一个reduce当中去)

为了解决这种情况,我们可以通过hive的rand函数,随记的给每一个为空的id赋上一个随机值,这样就不会造成数据倾斜
随机分布:

设置
set hive.exec.reducers.bytes.per.reducer=32123456;set mapreduce.job.reduces=7;
插入数据
INSERT OVERWRITE TABLE jointable查询数据
SELECT a.* FROM nullidtable a LEFT JOIN ori b ON  CASE WHEN  id IS NULL  THEN concat('hive', rand())  ELSE a.id  END  = b.id;
No rows affected (42.594 seconds)
  • 4)案例实操
    (0)需求:测试大表JOIN小表和小表JOIN大表的效率 (新的版本当中已经没有区别了,旧的版本当中需要使用小表)
    (1)建大表、小表和JOIN后表的语句
创建表
create table bigtable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';创建表
create table smalltable(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';创建表
create table jointable2(id bigint, time bigint, uid string, keyword string, url_rank int, click_num int, click_url string) row format delimited fields terminated by '\t';

(2)分别向大表和小表中导入数据

hive (default)> load data local inpath '/export/servers/hivedatas/big_data' into table bigtable;hive (default)>load data local inpath '/export/servers/hivedatas/small_data' into table smalltable;

(3)关闭mapjoin功能(默认是打开的)

set hive.auto.convert.join = false;

(4)执行小表JOIN大表语句

插入数据
INSERT OVERWRITE TABLE jointable2查询数据
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url FROM smalltable s left JOIN bigtable  b ON b.id = s.id;
Time taken: 67.411 seconds  

(5)执行大表JOIN小表语句

插入数据
INSERT OVERWRITE TABLE jointable2查询数据
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url FROM bigtable  b left JOIN smalltable  s ON s.id = b.id;
Time taken: 69.376seconds

可以看出大表join小表或者小表join大表,就算是关闭map端join的情况下,在新的版本当中基本上没有区别了(hive为了解决数据倾斜的问题,会自动进行过滤)

MapJoin

  • 如果**不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join(在Reduce阶段完成join)。容易发生数据倾斜。**可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。

1)开启MapJoin参数设置:
(1)设置自动选择Mapjoin

set hive.auto.convert.join = true; 默认为true

(2)大表小表的阈值设置(默认25M以下认为是小表):

set hive.mapjoin.smalltable.filesize=25123456;

2)MapJoin工作机制

首先是Task A,它是一个Local Task(在客户端本地执行的Task),负责扫描小表b的数据,将其转换成一个HashTable的数据结构,并写入本地的文件中,之后将该文件加载到DistributeCache中。
接下来是Task B,该任务是一个没有Reduce的MR,启动MapTasks扫描大表a,在Map阶段,根据a的每一条记录去和DistributeCache中b表对应的HashTable关联,并直接输出结果。
由于MapJoin没有Reduce,所以由Map直接输出结果文件,有多少个Map Task,就有多少个结果文件。
案例实操:
(1)开启Mapjoin功能

set hive.auto.convert.join = true; 默认为true

(2)执行小表JOIN大表语句

插入数据
INSERT OVERWRITE TABLE jointable2查询数据
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url FROM smalltable s JOIN bigtable  b ON s.id = b.id;
Time taken: 31.814 seconds

(3)执行大表JOIN小表语句

插入数据
INSERT OVERWRITE TABLE jointable2查询数据
SELECT b.id, b.time, b.uid, b.keyword, b.url_rank, b.click_num, b.click_url FROM bigtable  b JOIN smalltable  s  ON s.id = b.id;
Time taken: 28.46 seconds