[ 知识点 ] Hive数据倾斜

一、什么是数据倾斜

由于数据分布不均匀,造成数据大量的集中到一点,造成数据热点。

二、Hadoop 框架的特性

  • 不怕数据大,怕的是数据倾斜
  • Jobs 数较多的作业运行效率相对比较低,如子查询比较多
  • sum、count、max、min 等聚合函数,通常不会有数据倾斜问题

三、主要表现

  • 任务进度长时间维持在 99% 或者 100% 附近,查看任务监控界面,发现只有少量的 reduce 子任务未完成,因为其处理的数据量和其他的 reduce 差异过大。单一 reduce 处理的记录数和平均记录数相差不大。

四、容易数据倾斜情况

关键词情形后果
join其中一个表较小,但是key集中分发到某一个或者Reduce上的数据远高于平均值
大表与大表,但是分桶的判断字段0值或空值较多这些控制都是由一个reduce处理,非常慢
group by
group up by
维度过小,某值的数量过多处理某值的reduce非常耗时
Count Distinct某特殊值过多处理此特殊值的reduce耗时长
  • group by 不和聚合函数搭配的时候也容易出现数据倾斜
  • count(distinct),在数据量大的情况下,容易数据倾斜,因为 count(distinct) 是按 group by 字段分组,按 distinct 字段排序
  • 小表关联超大表 join

五、产生数据倾斜的原因

  • key 分布不均匀
  • 业务数据本身的特性
  • 建表考虑不周全
  • 某些 HQL 语句本身就存在数据倾斜

六、业务场景

(一)空值产生的数据倾斜

场景说明
在日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和用户表中的 user_id 相关联,就会碰到数据倾斜的问题。

解决方案
(1)user_id 为空的不参与关联
(2)赋予空值新的key值

总结
方法 2 比方法 1 效率更好,不但 IO 少了,而且作业数也少了,方案 1 中,log 表 读了两次,jobs 肯定是 2,而方案 2 是 1。这个优化适合无效 id(比如-99,’’,null)产生的数据倾斜,把空值的 key 变成一个字符串加上一个随机数,就能把造成数据倾斜的 数据分到不同的 reduce 上解决数据倾斜的问题。

改变之处:使本身为 null 的所有记录不会拥挤在同一个 reduceTask 了,会由于有替代的 随机字符串值,而分散到了多个 reduceTask 中了,由于 null 值关联不上,处理后并不影响最终结果。

(二)不同数据类型关联产生数据倾斜

场景说明
用户表中 user_id 字段为 int,log 表中 user_id 为既有 string 也有 int 的类型, 当按照两个表的 user_id 进行 join 操作的时候,默认的 hash 操作会按照 int 类型的 id 进 行分配,这样就会导致所有的 string 类型的 id 就被分到同一个 reducer 当中

解决方案
把数字类型 id 转换成 string 类型的 id

(三)大小表关联查询产生数据倾斜

注意:使用map join解决小表关联大表造成的数据倾斜问题。这个方法使用的频率很高。

map join 概念: 将其中做连接的小表(全量数据)分发到所有 MapTask 端进行 Join,从 而避免了 reduceTask,前提要求是内存足以装下该全量数据
在这里插入图片描述
以大表 a 和小表 b 为例,所有的 maptask 节点都装载小表 b 的所有数据,然后大表 a 的 一个数据块数据比如说是 a1 去跟 b 全量数据做链接,就省去了 reduce 做汇总的过程。 所以相对来说,在内存允许的条件下使用 map join 比直接使用 MapReduce 效率还高些, 当然这只限于做 join 查询的时候。

在 hive 中,直接提供了能够在 HQL 语句指定该次查询使用 map join,map join 的用法是 在查询/子查询的SELECT关键字后面添加/*+ MAPJOIN(tablelist) */提示优化器转化为map join(早期的 Hive 版本的优化器是不能自动优化 map join 的)。其中 tablelist 可以是一个 表,或以逗号连接的表的列表。tablelist 中的表将会读入内存,通常应该是将小表写在 这里。

在 hive0.11 版本以后会自动开启 map join 优化,由两个参数控制:
set hive.auto.convert.join=true; //设置 MapJoin 优化自动开启

set hive.mapjoin.smalltable.filesize=25000000 //设置小表不超过多大时开启 mapjoin 优化

如果是大大表关联呢?
那就大事化小,小事化了。把大表切分成小表,然后分别 map join,那么如果小表不大不小,那该如何处理呢???

七、Hive 参数优化

// 让可以不走mapreduce任务的,就不走mapreduce任务
hive> set hive.fetch.task.conversion=more;
 
// 开启任务并行执行
 set hive.exec.parallel=true;
// 解释:当一个sql中有多个job时候,且这多个job之间没有依赖,则可以让顺序执行变为并行执行(一般为用到union all的时候)
 
 // 同一个sql允许并行任务的最大线程数 
set hive.exec.parallel.thread.number=8;
 
// 设置jvm重用
// JVM重用对hive的性能具有非常大的 影响,特别是对于很难避免小文件的场景或者task特别多的场景,这类场景大多数执行时间都很短。jvm的启动过程可能会造成相当大的开销,尤其是执行的job包含有成千上万个task任务的情况。
set mapred.job.reuse.jvm.num.tasks=10; 
 
// 合理设置reduce的数目
// 方法1:调整每个reduce所接受的数据量大小
set hive.exec.reducers.bytes.per.reducer=500000000;500M)
// 方法2:直接设置reduce数量
set mapred.reduce.tasks = 20

// map端聚合,降低传给reduce的数据量

set hive.map.aggr=true  
// 开启hive内置的数倾优化机制
set hive.groupby.skewindata=true

八、SQL优化

where 条件优化

优化前:RDB不用考虑会自动优化

select m.cid,u.id 
from order m 
join customer u 
on( m.cid =u.id )
where m.dt='20180808';

优化后:where 条件在 map 端执行而不是在 reduce 端执行

select m.cid,u.id 
from (
	select * 
	from order 
	where dt='20180818'
) m join customer u 
on( m.cid =u.id);

union 优化

尽量不要使用union (union 去掉重复的记录)而是使用 union all 然后在用 group by 去重


count distinct 优化

不要使用count (distinct cloumn) ,使用子查询

select count(1) 
from (
	select id 
	from tablename 
	group by id
) tmp;

用 in 来代替 join

如果需要根据一个表的字段来约束另为一个表,尽量用in来代替 join . in 要比 join 快

select id,name from tb1  a join tb2 b on(a.id = b.id);
 
select id,name from tb1 where id in(select id from tb2);

优化子查询

消灭子查询内的 group by 、 COUNT(DISTINCT),MAX,MIN。可以减少job的数量。


job 优化
Common/shuffle/Reduce JOIN 连接发生的阶段,发生在reduce 阶段, 适用于大表 连接 大表(默认的方式)

Map join :连接发生在map阶段 , 适用于小表 连接 大表
大表的数据从文件中读取
小表的数据存放在内存中(hive中已经自动进行了优化,自动判断小表,然后进行缓存)

set hive.auto.convert.join=true;

SMB join
Sort -Merge -Bucket Join 对大表连接大表的优化,用桶表的概念来进行优化。在一个桶内发生笛卡尔积连接(需要是两个桶表进行join)

set hive.auto.convert.sortmerge.join=true;  
set hive.optimize.bucketmapjoin = true;  
set hive.optimize.bucketmapjoin.sortedmerge = true;  
set hive.auto.convert.sortmerge.join.noconditionaltask=true;

九、合并小文件

  • 设置map输入的小文件合并

set mapred.max.split.size=256000000;  
//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000;
//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)  
set mapred.min.split.size.per.rack=100000000;
//执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
  • 设置map输出和reduce输出进行合并的相关参数
//设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
//设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
//设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000
//当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
set hive.merge.smallfiles.avgsize=16000000

十、查看sql的执行计划

explain sql
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值