hive数据倾斜

244 阅读4分钟

了解hive

Map:Map是Hive查询执行过程中第一个阶段的操作。它负责处理输入数据并将其转换为键值对(key-value pairs)。在这个阶段,数据被分割成小块并并行处理。每个map任务处理一个数据分片,将其转换为中间结果

Reducer:Reducer是Hive查询执行过程中的第二个阶段。它接收来自map任务的中间结果,进行进一步处理并生成最终结果。Reducer的主要功能是聚合、过滤或排序数据,通常是在map输出的基础上进行操作

Reducer Task: Reducer Task指的是具体执行Reducer操作的任务。每个Reducer Task负责处理一部分map输出的数据。Hive会根据数据的分布和Reducer的数量来决定创建多少个Reducer Task。每个Reducer Task会在集群中独立运行,通常是并行执行的

总结来说,Map负责初步处理和转换数据,Reducer负责进一步聚合和生成最终结果,而Reducer Task则是具体执行这些操作的任务单位

什么是数据倾斜

有些Reducer节点被分配的键值对太多,有些Reducer则很少,导致处理数据时有些很快处理完,有些很慢,都在等这个超大的key值,导致整体任务迟迟未能完成。

常见表现为,map任务已执行完毕,个别reduce task长时间卡在99%

出现数据倾斜的原因

根本原因:key分布不均匀。为什么key分布不均匀?

  • 业务数据本身特点:某些业务数据作为key的字段本身很集中,有大量重复值
  • 建表问题:比如日志表都是某些字段,导致含有大量空值,如又作为join关联条件
  • sql语句问题:某些sql语句本身就有数据倾斜,比如计数时用distinct,count(distinct),如果某些特殊值过多,则该值会被分入同一个reduce,导致耗时

解决方法

设置常用参数

-- 开启map端combiner聚合功能 set hive.map.aggr=true --开启负载均衡(万能方法) set hive.groupby.skewindata=true --增加reducer任务数量(拉取数据分流) set mapred.reduce.tasks=20; --合并小文件 set hive.merge.mapfiles=true

SQL语句调整

少用count distinct

尽量采用 sum group by 的方式来替换count(distinct)

--before select a,count(distinct b) as c from tb group by a; --after select a,count(1) as c from ( select a,b,sum(1) from tb group by a,b ) t group by a;

保证关联条件数据类型一致

不同数据类型关联时可能会产生数据倾斜现象。

例如,按照user_id对两个表Join操作时,因为连接时要进行user_id的比较,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中,可能会导致数据倾斜

select * from a left outer join b on a.user_id = cast(b.user_id as string)

大小表join

可以使用map join让小维度表先进内存,即在map端完成join,不经过reduce

大表Join大表

  • 如果是不需要空值的内连接,有2种处理方法
    • 空值不参与关联:可以内层通过where条件筛选掉空值
    • 给空值分配随机的key值:可以对识别出的空值key变成一个字符串+随机数,这样就可以将原来集中的key分散开来,避免了数据倾斜的风险,且由于是无效数据无法关联上,因此不会出现在结果表中
  • 如果需要空值的左连接,即需要保留主表的无效数据,只需要将上述方法得到的结果再与驱动表的这些无数据取并集就可以
select * from log a left outer join users b on a.user_id is not null and a.user_id=b.user_id union all select * from log a where a.user_id is null;

特殊情况

有时,group by时维度过小的话,数据过于集中,数据自身倾斜,在SQL逻辑优化效果的不大情况下,有时候是可以将倾斜的key单独拿出来处理,即将大key和其他key分开处理,最后union回去。业务情景举例:比如 上海的用户比其它地方的用户多很多,此时可以把上海的数据单独处理,先把的数据分成N块,每块的数据进行局部统计,再将每块的局部统计结果进行汇总,最终统计出结果。例如,

select a.city,a.uv,b.level from ( select city,count(user_id) as uv from a where city = 'Shanghai' gorup by city ) a left outer join b on a.city=b.city union all select a.city,a.uv,b.level from( select city,count(user_id) as uv from a where city <> 'Shanghai' group by city )a left outer join b on a.city=b.city