hive问题总结

1. 任务执行过程中爆出java heap space

有三个阶段均会发生该错误,首先判断任务运行到哪个阶段报错内存不足。
map阶段、shuffle阶段、reduce阶段。
map阶段:

一般是发生mapjoin才会产生OOM。
    通过设置参 set hive.auto.convert.join=false 转换成reduce端的common join。
如果common join报oom,说明切片过大。因为hdfs显示的是压缩后的大小,解压切片后会撑爆内存。
    这个时候需要调整如下参数来调小map端的输入量
    set mapred.max.split.size=256000000;
    set mapred.min.split.size=10000000;
    set mapred.min.split.size.per.node=80000000;
    set mapred.min.split.size.per.rack=80000000;

shuffle阶段

一般是由于map的输出较大,shuffle阶段选择拷贝map输出到内存导致。
    降低单个shuffle能够消耗的内存占reduce所有内存的比例(set mapreduce.reduce.shuffle.memory.limit.percent=0.10),使得shuffle阶段拷贝map输出时选择落磁。

reduce阶段

单个reduce处理数据量过大,通过设置参数修改reduce个数或者减少每个reduce的聚合数据量
    修改reduce个数:
    set mapred.reduce.tasks  或者    set mapreduce.job.reduces

    减少ruduce聚合数据量
    set hive.exec.reducers.bytes.per.reducer=300000000;

2. 数据倾斜

2.1 判断数据倾斜

2.1.1 map阶段处理时间过长

不可分割的文件引发数据倾斜:
	压缩算法的不同会造成hdfs上的文件不可切分,无法分片,就只能被一个map获取。例如:GZIP
	解决办法:需要将不能切分的文件转换为bzip2或者zip的可压缩算法

2.1.2 reduce阶段处理很慢

测试数据是A、B两表,A中id存在大量null值,B中id无null值。并且id为int类型

2.1.2.1 null值过多导致倾斜

在这里插入图片描述

对于null值的出现针对inner join以及left join的计算做了如上统计,可以清晰的去判断什么情况下会有倾斜产生,并且采用哪种方式去处理倾斜。具体的sql语句如下:

1、先处理非null值再union all NULL数据
select
    s1.id,
    s1.name,
    s2.value
from skewdata_A s1
 join skewdata_B s2 on s1.id=s2.id
where s1.id is not null
union all 
select
	id,
	name,
	null
from skewdata_A
where id is null;
这个语句在做inner join时候也可以不加is not null

2on条件对null值做随机数处理后再关联
select
    s1.id,
    s1.name,
    s2.value
from skewdata_A s1
left join skewdata_B s2 on coalesce(cast(s1.id as string),concat('hive_',rand()))=cast(s2.id as string);
此处是因为等号两边的数据类型不一致,做了统一处理。
2.1.2.2 关联key数据类型不一致导致倾斜

还是上面的例子,以下是一个对比图
在这里插入图片描述
可以看出当两边数据类型int、string不一致,对string类型的id进行hash,默认的hash操作会按int类型的id进行分配,然后string类型的id都会被分配到同一个id 0 ,从而进入到同一个redce数据倾斜。需要使用cast保证两边数据类型一致,处理方法同上
在这里插入图片描述在这里插入图片描述
nvl(col1,col2)两个参数数据类型是一致的
coalesce(col1,col2,col3…),数据类型可以不一致

2.1.2.3 热点key

如果业务数据本身存在热点key,即高频访问key这样的特性,key本身就分布不均匀,那么mr join操作的时候必然会引起数据倾斜。

一:如果是单表groupby倾斜优先开启负载均衡
set hive.map.aggr=true; --map端的combiner,默认为true
set hive.groupby.skewindata=true; --开启负载均衡
set hive.groupby.mapper; --配置map端的聚合条数,可不设置

这样 MapReduce 进程则会生成两个额外的 MR Job,这两个任务的主要操作如下:

  • 第一步:MR Job 中Map 输出的结果集合首先会随机分配到 Reduce 中,然后每个 Reduce 做局部聚合操作并输出结果,这样处理的原因是相同的Group By Key有可能被分发到不同的 Reduce Job中,从而达到负载均衡的目的。
    在这里插入图片描述
  • 第二步:MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成聚合操作。
    如下是一个普通的group by ,观察一下第一次的mr运行情况
    在这里插入图片描述
    如果是join倾斜,也可以通过设置来解决
set hive.skewjoin.key=10000; --join 的键对应的记录条数超过这个值则会进行分拆
set hive.optimize.skewjoin=true; --join发生倾斜开启
set hive.skewjoin.mapjoin.map.tasks; --控制第二个任务的mapper数量,默认是10000;
  • 如果开启第二个参数,hive会将计数超过第一个参数的key对应的记录临时写到文件中
  • 然后启动另一个job做map join生成结果
二:使用mapjoin(适用于小表join大表,某个key过大)

mapjoin原理就是将倾斜的数据存到分布式缓存中,分发到各个Map任务所在节点,在map端完成join,没有后续shuffle以及reduce阶段,避免数据倾斜,以及提高效率。
不管是小表join大表还是大表join小表,只要小表是使用mapjoin,流程如下:

  • 大表的数据块被分布在多个计算节点上
  • 小表加载到内存中,并广播到各个计算节点
  • 每个计算节点会对小表构建一个索引结构(哈希表或者排序树)
  • 对于每个大表数据块,提取关键键或者条件字段
  • 使用索引结构去匹配上一步的关联键
  • 匹配成功,就在两张表中找到了相同的关联键值,计算节点根据关联的键值去进行关联处理,获取记录结果
  • 最后将各个计算节点的结果reduce合并
    在这里插入图片描述
注:可以看到map的输入记录只有55条,而理论来说肯定是不止这些的,有点奇怪。

hive0.11版本之后自动开始mapjoin
如果遇到系统没有推测执行mapjoin的,可以显式执行mapjoin。设置的参数配置如下:

set hive.auto.convert.join=true; --关闭自动mapjoin
set hive.ignore.mapjoin.hint=false; --不忽略mapjoin标识
set mapreduce.map.memory.mb=100; --调整map端的内存大小,优化内存溢出
set hive.mapjoin.smalltable.filesize=10000000; --mapjoin表的阈值
三:count(distinct)造成任务缓慢

原因:如果sql语句仅有这个count,没有groupby。distinct本身有一个全局排序的过程,这样只会产生一个reduce,运行缓慢。而加入有groupby,那样又会存在某一个键对应的其他字段有大量重复数据,这样子导致该reduce缓慢,造成数据倾斜
sql语句如下:(

-- 数据倾斜
select a,count(distinct b) from t group by a;
-- 先去重后分组
select a,count(1) from (select a, b from t group by a,b) group by a;
四:多维聚合运算

如果聚合字段过多,容易引发job作业的map端输出数据膨胀,从而内存溢出。如果还存在热点key,还会加剧数据倾斜问题
多维运算如:with rollup,grouping sets,cubes
解决方法两种:

  • 拆分多维运算为多个union all + group by。这样子将启动多个job,减少单个job的map端的输出数据量
  • 但是一旦聚合字段过多,就要写很多个union all,不方便。引用一个配置如下,该配置自动控制作业的拆解,默认值为30。如果键组合数量超过该值,那会新启动一个job来处理之后的键组合,这样做是为避免单个job的压力过大。另外如果单个作业内有稍大的数据倾斜,将该值调小可以减弱数据倾斜的影响!
hive.new.job.grouping.set.cardinality
五:增加reduce个数

这样做适合于出现了多个大key,reduce数量增多,可以避免多个大key落到了同一个reduce中

set mapred.reduce.tasks=10;
或者
set mapreduce.job.reduces=10;
六:调整reduce运行内存
set mapreduce.reduce.memory.mb=4096; --设置reduce task的内存
set mapreduce.reduce.java.opts=-Xmx5000m -XX:MaxPermSize=128m --表示reduce任务的最大堆内存是5G,永久代空间是128M

3、谓词下推问题以及CBO的影响

在分析之前首先,明确一下概念以及在没有cbo优化时候两个原则(有了cbo之后join之后的谓词有点不太适用,也会进行下推):

概念:
	left join左侧名表名称:保留表
	右侧表名称:空表(null supplying table) 
	join中谓词:就是on条件之后的and 条件
	join之后的谓词:在where中的条件
原则:
	在join中的谓词如果是保留表的,则不会进行下推。
	在join后的谓词如果是null supplying table的,则不会进行下推。

使用代码

1. outer join 
	目标:sql_a 对比 sql_b 验证cbo开启后谓词在sql中位置是否影响下推以及结果;
		  对比cbo开启前后 sql_b 语句的谓词下推的生效与否。
	测试sql:	  
		sql_a:select t1.*,t2.* from test1 t1 left join test2 t2 on t1.id=t2.id and (t2.openid='apple' or t2.openid='lemon') where t1.openid='pear';  
		sql_b:select t1.*,t2.* from test1 t1 left join test2 t2 on t1.id=t2.id  where t1.openid='pear' and (t2.openid='apple' or t2.openid='lemon'); 

2. full join 
	目标:sql_c 对比 sql_d 、sql_e 验证cbo开启后谓词在sql中位置是否影响下推以及结果;
		  对比cbo开启前后 sql_d 语句的谓词下推的生效与否。
	测试sql:
		sql_c:select t1.*,t2.* from test1 t1 full join test2 t2 on t1.id=t2.id and (t2.openid='apple' or t2.openid='lemon') where t1.openid='pear';
		sql_d:select t1.*,t2.* from test1 t1 full join test2 t2 on t1.id=t2.id  where t1.openid='pear' and (t2.openid='apple' or t2.openid='lemon'); 
		sql_e:select t1.*,t2.* from (select * from test1 where openid='pear') t1 full join test2 t2 on t1.id=t2.id and (t2.openid='apple' or t2.openid='lemon') ; 

两张表数据如下:
在这里插入图片描述
通过实际运行后结果如下:

outer join

开启cbo的 sql_a 执行结果

select t1.*,t2.* from test1 t1 left join test2 t2 on t1.id=t2.id and (t2.openid='apple' or t2.openid='lemon') where t1.openid='pear'; 

在这里插入图片描述

开启cbo的 sql_b 执行结果

select t1.*,t2.* from test1 t1 left join test2 t2 on t1.id=t2.id  where t1.openid='pear' and (t2.openid='apple' or t2.openid='lemon'); 

在这里插入图片描述

关闭cbo的 sql_b 执行结果

select t1.*,t2.* from test1 t1 left join test2 t2 on t1.id=t2.id  where t1.openid='pear' and (t2.openid='apple' or t2.openid='lemon'); 

在这里插入图片描述

结论

1.sql_a 对比 sql_b 验证cbo开启后谓词在sql中位置是否影响下推以及结果:空表的谓词放置顺序不影响下推,但影响结果;虽然确实都会下推,但是在下推的同时对出现在join之后的属于空表的谓词增加了条件,id is not null。正式因为这个原因导致了结果的不一致。
2.对比cbo开启前后 sql_b 语句的谓词下推的生效与否:cbo确实对在join之后的谓词均进行下推。

full join

开启cbo的 sql_c 执行结果

select t1.*,t2.* from test1 t1 full join test2 t2 on t1.id=t2.id and (t2.openid='apple' or t2.openid='lemon') where t1.openid='pear';

在这里插入图片描述

开启cbo的 sql_d 执行结果

select t1.*,t2.* from test1 t1 full join test2 t2 on t1.id=t2.id  where t1.openid='pear' and (t2.openid='apple' or t2.openid='lemon'); 

在这里插入图片描述

关闭cbo的 sql_d执行结果

select t1.*,t2.* from test1 t1 full join test2 t2 on t1.id=t2.id  where t1.openid='pear' and (t2.openid='apple' or t2.openid='lemon'); 

在这里插入图片描述

开启cbo的 sql_e 执行结果

select t1.*,t2.* from (select * from test1 where openid='pear') t1 full join test2 t2 on t1.id=t2.id and (t2.openid='apple' or t2.openid='lemon') ; 

在这里插入图片描述

结论

1.sql_c 对比 sql_d 、sql_e 验证cbo开启后谓词在sql中位置是否影响下推以及结果:可以观察到sql_c、sql_d均进行了谓词下推,而sql_e却没有,理论上如果sql_d可以进行谓词下推+id is not null, 那sql_e应该也可以进行谓词下推,只是不加is not null而已。这点很疑惑。
2.对比cbo开启前后 sql_d 语句的谓词下推的生效与否:此处可以就看到cbo开启后两张表虽然同时是空表以及保留表,但是都进行谓词下推了。并且此处谓词下推就不符合一开始提到的两个原则了。

4、数据存在特殊ascii码、unicode码

主要是数据含有一些特殊字符,清洗时需要过滤的

  • 非断空格 0xa0、\u00A0
select replace(id,'\u00A0','');
  • BOM标识符 0xfeff
select  '\ufeff'

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值