真是好久没写博客了。。。趁着今天休息,把以前遇到的问题整理一下吧。
互联网中的数据量通常会很大,比如埋点数据。。。因此在日常数据处理中就会遇到数据倾斜的问题,就是那种跑半天跑不出数据或者reduce阶段卡在99%的那种情况。。。今天来说一下日常遇到数据倾斜的解决办法!
本文将从一下几个方面进行总结:
1、去重问题:
1.1 union-union all(子查询实现job的并行运算)
union 和union all的区别就是union具有去重的功能,且是一个全局的去重的过程,因为去重就会涉及到一个排序的问题,因此在使用union时效率会比较低。
1.2 distinct 和 group by
在日常使用中,不建议使用distinct 进行去重,特别针对于埋点数据,通常会用group by进行替代。还有一中场景通常是要进行去重然后进行计数,比如计算UV等数据,可以和union 和group by 结合使用。
案例:
--统计多个页面汇总的UV
SELECT COUNT(*) AS UV
FROM
( SELECT clientcode
FROM
( SELECT clientcode FROM dwhtl.maidian_page_data_01
UNION ALL
SELECT clientcode FROM dwhtl.maidian_page_data_02
) a
GROUP BY clientcode
) a
上面的代码写法,在MapReduce阶段,group by已经对clientcode进行去重了,最后统计的就直接是去重后的数据。但是如果直接用count(distinct clientcode)的话,会有一个全局排序的过程,效率会很低。
2、JOB数的控制
应尽可能的减少job数而提高查询速度。
2.1 使用union all减少job数
如果union all查询语句中有group by ,它仍然是多个job,并没有达到优化的效果。
SELECT pagecode
,COUNT(*) AS num
FROM
( SELECT pagecode,clientcode FROM dwhtl.maidian_data_page_01
UNION ALL
SELECT pagecode,clientcode FROM dwhtl.maidian_data_page_02
UNION ALL
SELECT pagecode,clientcode FROM dwhtl.maidian_data_page_03
) a
GROUP BY pagecode
上述写法,只有一个job,因为在map阶段,已经融合了所有的数据。
2.2 多表连接时,同join条件,也会减少job数。
即在多张表进行连接时,主表的和多个副表的连接条件相同时,只用一个job就解决了。
2.3 设置合理的map reduce的task数,能有效提升性能。
具体参数,见https://blog.csdn.net/weixin_37536446/article/details/82462742
3、数据值&数据类型的问题
3.1 NULL值问题
在实际使用中,如果null值较多,值为null的会人道一个ruduce中进行处理,导致这一个的ruduce处理的量过大,产生数据倾斜。注:如果这个字段不是join、group by 的条件,就不会因为它而产生倾斜。
解决办法1:将任务拆开,即先将连接的字段值为null的过滤掉(限制条件is not null),然后用union all将过滤掉的数据(限制条件is null)合到一起。
解决办法2:利用随机函数,将为null的数据随机分布到不同的reduce中。因为空值不参与关联,即使分到不同 的 Reduce 上,也不会影响最终的结果。即在连接时,连接条件如下:
ON CASE WHEN a.user_id IS NULL THEN CONCAT(‘dp_hive’,RAND()) ELSE a.user_id END =b.user_id;
3.2 数据类型不一致。
在进行连接操作时,将所连接的数据类型进行转换,比如: on a.date = cast(b.date as string)
3.3 业务本身的数据导致
比如在全国的流量时,北京的人流量是其他地方的综合还要多,这个时候如果按照地区进行统计就会产生数据倾斜的问题。
解决办法:添加job数,增加随机列。
4、hive策略优化
在写SQL时,应尽量避免不必要的数据读入,比如字段、分区等。
4.1 列裁剪
Hive 在读数据的时候,只读取查询中所需要用到的列,而忽略其它列,这能够有效的节省了读取开销,中间表存储开销和数据整合开销。
裁剪所对应的参数项为:hive.optimize.cp=true(默认值为真)
4.2 分区裁剪
在查询时,避免读取全部的分区,只读取自己想要的分区数据。
分区参数为:hive.optimize.pruner=true(默认值为真)
4.3 JOIN操作
SQL中带有 join 操作的代码语句时,应该将条目少的表/子查询放在 Join 操作符的左边。 因为在 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,载入条目较少的表 可以有效减少 OOM(out of memory)即内存溢出。
5、控制任务数
5.1 map控制(太少了会造成并发数,太多了会浪费大量的内存。)
1、每个map最大输入大小:SET mapred.max.split.size = 256000000;
即(256M),现在假如输入有1024M,这样就会被拆成4个map,1028M则会有5个map
2、一个节点上split的至少的大小:SET mapred.min.split.size.per.node=100000000(100M)
4是进行小文件合并,2是判断把怎么样的小文件合并。
3、执行map前进行小文件合并:
SET hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
5.2 reduce控制
1、每个reduce处理的数据量
SET hive.exec.reducers.bytes.per.reducer = 500000000;
即(500M)一般情况下设置这一个就ok了
2、制定reduce数量
SET mapred.reduce.tasks = 20;
3、reduce输出文件数
在Map-only的任务结束时合并小文件
SET hive.merge.mapfiles = true
在Map-Reduce的任务结束时合并小文件
SET hive.merge.mapredfiles = true
合并文件的大小
SET hive.merge.size.per.task = 256*1000*100
输出文件的平均值大小小于该值时,启动一个独立的map-reduce任务进行文件合并
SET hive.merge.smallfiles.avgsize = 16000000(16M)
以上就是在工作中遇到的数据倾斜问题,也给出了解决方法,欢迎相互交流~