数据倾斜的原理及解决办法

数据倾斜

一.什么是数据倾斜

简单来说,数据倾斜就是在计算数据的时候,由于key的分布不均匀,导致大量的数据集中到一台或者几台机器上计算,这些数据的计算速度远远低于平均计算速度,导致整个计算过程很慢。

二.原因

1.操作

在这里插入图片描述

2.原因

1)、key分布不均匀

2)、业务数据本身的特性

3)、建表时考虑不周

4)、某些SQL语句本身就有数据倾斜

三.数据倾斜的表现

1.hadoop中的数据倾斜

hadoop中直接贴近用户使用的是Mapreduce程序和hive程序。数据倾斜时主要表现在reduce阶段卡在99%,一直不能结束。
详细日志表现为:
(1)有一个或多个reduce卡住。
(2)各种container报错。
(3)读写的数据量极大,远超其他正常的reduce,伴随着数据倾斜,会出现任务被kill等各种诡异的现象。
经验:
hive的数据倾斜一般发送在sql中group和on上,而且和数据逻辑绑定比较深

2.spark中的数据倾斜

spark中的数据倾斜主要包括Spark Streaming和Spark Sql,表现有下面几种
(1)Executor lost,OOM,Shuffle过程出错
(2)Driver OOM
(3)单个Executor执行时间过长,整体任务卡在某个阶段不能结束。
(4)正常运行的任务突然失败
补充: 在spark streaming程序中,数据倾斜更容易出现,特别时在程序中包含一些类似sql的join、group这种操作的时候。因为spark streaming 程序在运行的时候,我们一般不会分配特别多的内存,因此一旦这个过程出现一些数据倾斜,就十分容易造成OOM。

三.数据倾斜的原理

在进行shuffle的时候,必须将各个节点上相同的Key拉取到某个节点上的一个task来进行处理,比如按照key进行聚合或者join操作。如果某个key对应的数据量特别大的话,会发生数据倾斜。比如大部分key对应的10条数据,但个别key却对应了100万条数据,那么大部分task会只分配到10条数据,而个别task可能会分配了100万数据。整个spark作业的运行进度是由运行时间最长的那个task决定的。
因此出现数据倾斜的时候,spark作业看起来会运行得非常缓慢,甚至可能因为某个task处理的数据量过大导致OOM。

四.数据倾斜的解决办法

1.增加reduce的jvm内存

在硬件允许的情况下增加reduce内存大小。不过也有一定限制:
(1)内存的限制存在
(2)可能对集群其他任务的运行产生不稳定的影响

2.增加reduce个数

可以缓解偶然情况下的某些reduce不小心分配了较多记录数的情况。

3.customer partition

已知数据分布的显著类型,实现对应的partitioner也是一个很好的方法。

4.参数调节

4.1hive.map.aggr = true

Map 端部分聚合,相当于Combiner

4.2hive.groupby.skewindata=true

有数据倾斜的时候进行负载均衡,当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。
4.3.根据实际需求调整map和reduce数量
4.4 开启hive执行优化,默认为true
实现大小表join的时候,自动加小表加载到内存里,小表默认大小为25M,可以通过参数设置小表大小

5.hive skewed join

目前hive 有几个正在开发中的处理skewed join 情况的jira case, HIVE-3086 , HIVE-3286 ,HIVE-3026 . 简单介绍一下就是facebook 希望通过手工处理提前枚举的方式列出单个倾斜的值,在join 的时候将这些值特殊列出当作map join 来处理,对于其他值使用原来的方式. 我个人觉得这太不伸缩了,值本身没有考虑应用过滤条件和优化方式之后的数据量大小问题,他们提前列出的值都是基于整个分区的. join key 如果为组合key 的情况也应该没有考虑,对metastore 的储存问题有限制,对输入的大表和小表都会scan 两次( 一次处理非skew key , 一次处理skew key 做map join), 对输出表也会scan 两次(将两个结果进行merge) , skew key 必须提前手工列出这又存在额外维护的成本,目前因为还没有完整的开发完到能够投入生产的情况,所以等所有特性处理完了有了文档在看看这个处理方式是否有效,我个人认为的思路应该是接着bucked map join 的思路往下走,只不过不用提前处理cluster key 的问题, 这时候cluster key 的选择应该是join key + 某个能分散join key 的列, 这等于将大表的同一个key 的值分散到了多个不同的reduce 中,而小表的join key 也必须cluster 到跟大表对应的同一个key , join 中对于数据分布第二种情况不用太难,增加reduce 个数就好,主要是第一种,需要大表的join key 能够分散,对于同样join key 的小表又能够匹配到所有大表中的记录. 这种思路就是不用扫描大表两遍或者结果输出表,不需要提前手工处理,数据是动态sample 的应用了过滤条件之后的数据,而不是提前基于统计数据的不准确结果. 这个基本思路跟tenzing 里面描述的distributed hash join 是一样的,想办法切成合适的大小然后用hash 和 map join .

6.pipeline

当同时出现join 和group 的时候, 那么这两个操作应该是以pipeline (管道) 的方式执行. 在join 的时候就可以直接使用group 的操作符减少大量的数据,而不是等待join 完成,然后写入磁盘,group 又读取磁盘做group操作. HIVE-2206 正在做这个优化. hive 里面是没有pipeline 这个概念的. 像是cloudera 的crunch 或者twitter 的Scalding 都是有这种概念的.

7.sql语句

7.1 distinct
distinct 本身就是group by 的一种简写,我原先以为count(distinct x)这种跟group by 是一样的,但是发现hive 里面distinct 明显比group by 要慢,可能跟group by 会有map 端的combiner有关, 另外观察到hive 在预估count(distinct x) 的reduce 个数比group by 的个数要少 , 所以hive 中使用count(distinct x) , 要么尽量把reduce 个数设置大,直接设置reduce 个数或者hive.exec.reducers.bytes.per.reducer 调小,我个人比较喜欢调后面一个,hive 目前的reduce 个数没有统计信息的情况下就是用map端输入之前的数值, 如果你是join 之后还用count(distinct x) 的话,这个默认值一般都会悲剧,如果有where 条件并能过滤一定数量的数据,那么默认reduce 个数可能就还好一点. 不管怎样,多浪费一点reduce slot 总比等十几甚至几十分钟要好, 或者转换成group by 的写法也不错,写成group by 的时候distributed by 也很有帮助.
7.2 特殊值(null)
(1)count distinct时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。
(2)join时,
解决方法1:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。
解决方法2: 空值不参与关联
7.3 不同数据类型关联产生数据倾斜
场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。
解决方法:把数字类型转换成字符串类型
7.4 join
(1)关于驱动表的选取,选用join key分布最均匀的表作为驱动表
做好列裁剪和filter操作,以达到两表做join的时候,数据量相对变小的效果。
(2)小表不小不大,怎么用 map join 解决倾斜问题
使用 map join 解决小表(记录数少)关联大表的数据倾斜问题,这个方法使用的频率非常高,但如果小表很大,大到map join会出现bug或异常,这时就需要特别的处理。 以下例子:
select * from log a
left outer join users b
on a.user_id = b.user_id;
users 表有 600w+ 的记录,把 users 分发到所有的 map 上也是个不小的开销,而且 map join 不支持这么大的小表。如果用普通的 join,又会碰到数据倾斜的问题。
解决方法:
select /+mapjoin(x)/* from log a
left outer join (
select /+mapjoin©/d.*
from ( select distinct user_id from log ) c
join users d
on c.user_id = d.user_id
) x
on a.user_id = b.user_id;
假如,log里user_id有上百万个,这就又回到原来map join问题。所幸,每日的会员uv不会太多,有交易的会员不会太多,有点击的会员不会太多,有佣金的会员不会太多等等。所以这个方法能解决很多场景下的数据倾斜问题。
(3) 在判断小表不大于1G的情况下,使用map join
7.5 group by维度过小:

采用sum() group by的方式来替换count(distinct)完成计算。

7.6 特殊情况特殊处理:

在业务逻辑优化效果的不大情况下,有些时候是可以将倾斜的数据单独拿出来处理。最后union回去。

8.index尤其是bitmap index

hive 中的index 就是物化视图,对于group by 和distinct 的情况等于变成了map 端在做计算,自然不存在倾斜. 尤其是bitmap index , 对于唯一值比较少的列优势更大,不过index 麻烦的地方在于需要判断你的sql 是不是常用sql , 另外如果create index 的时候没有选你查询的时候用的字段,这个index 是不能用的( hive 中是永远不可能有DBMS中的用index 去lookup 或者join 原始表这种概念的)

总结:
一.数据倾斜没有一劳永逸的方式可以解决,了解你的数据集的分布情况,然后了解你所使用计算框架的运行机制和瓶颈,针对特定的情况做特定的优化,做多种尝试,观察是否有效
二.使map的输出数据更均匀的分布到reduce中去,是我们的最终目标。由于Hash算法的局限性,按key Hash会或多或少的造成数据倾斜。大量经验表明数据倾斜的原因是人为的建表疏忽或业务逻辑可以规避的。在此给出较为通用的步骤:
1、采样log表,哪些user_id比较倾斜,得到一个结果表tmp1。由于对计算框架来说,所有的数据过来,他都是不知道数据分布情况的,所以采样是并不可少的。
2、数据的分布符合社会学统计规则,贫富不均。倾斜的key不会太多,就像一个社会的富人不多,奇特的人不多一样。所以tmp1记录数会很少。把tmp1和users做map join生成tmp2,把tmp2读到distribute file cache。这是一个map过程。
3、map读入users和log,假如记录来自log,则检查user_id是否在tmp2里,如果是,输出到本地文件a,否则生成

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hive数据倾斜是指在Hive查询过程中,某些任务的处理时间比其他任务长得多,导致整个查询变得很慢。这通常是由于数据分布不均匀造成的。下面介绍一些常用的Hive数据倾斜解决方法: 1. 动态分区 动态分区是一种Hive优化技术,它可以将数据分布到不同的分区中,以避免数据倾斜。在动态分区中,Hive会根据查询条件自动创建分区,并将数据插入到对应的分区中。这样可以使数据分布更加均匀,减少数据倾斜的问题。 2. 桶 桶是一种将数据分布到多个文件中的技术。在Hive中,可以使用桶来将数据分布到多个文件中,以避免数据倾斜。桶的原理是先将数据按照某个字段进行哈希,然后将哈希值相同的数据插入到同一个文件中。这样可以让数据更加均匀地分布到多个文件中,减少数据倾斜的问题。 3. 调整并行度 调整并行度是指调整Hive查询的任务数,以避免数据倾斜。当某些任务的处理时间比其他任务长得多时,可以尝试将任务数增加或减少,以重新分配负载。这样可以使查询更加均衡,减少数据倾斜的问题。 4. 重构SQL 如果上述方法无法解决数据倾斜问题,可以尝试重构SQL。根据具体的查询需求,可以尝试改变查询条件或者使用其他方式查询数据。这样可以减少查询的数据量,避免数据倾斜的问题。 总之,Hive数据倾斜是一个常见的问题,但是通过一些优化技术和合理的调整,可以有效地解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值