少数介绍数据歪斜的文章都是以大篇幅的实践为主,并没有给出具体的数据歪斜案例。当工作中遇到了歪斜问题,这些实践很难间接利用,导致咱们面对歪斜时还是手足无措。
明天咱们不扯大篇实践,间接以例子来实际,排查是否呈现了数据歪斜,具体是哪段代码导致的歪斜,怎么解决这段代码的歪斜。
当执行过程中工作卡在 99%,大概率是呈现了数据歪斜,然而通常咱们的 SQL 很大,须要判断出是哪段代码导致的歪斜,能力利于咱们解决歪斜。通过上面这个非常简单的例子来看下如何定位产生数据歪斜的代码。
表构造形容
先来理解下这些表中咱们须要用的字段及数据量:
表的字段十分多,此处仅列出咱们须要的字段
第一张表:user_info(用户信息表,用户粒度)
字段名 字段含意 字段形容
userkey 用户 key 用户标识
idno 用户的身份证号 用户实名认证时获取
phone 用户的手机号 用户注册时的手机号
name 用户的姓名 用户的姓名
user_info 表的数据量:1.02 亿,大小:13.9G,所占空间:41.7G(HDFS 三正本)
第二张表:user_active(用户沉闷表,用户粒度)
字段名 字段含意 字段形容
userkey 用户 key 用户没有注册会调配一个 key
user_active_at 用户的最初沉闷日期 从埋点日志表中获取用户的最初沉闷日期
user_active 表的数据量:1.1 亿
第三张表:user_intend(用户意向表,此处只取近六个月的数据,用户粒度)
字段名 字段含意 字段形容
phone 用户的手机号 有动向的用户必须是手机号注册的用户
intend_commodity 用户动向次数最多的商品 客户对某件商品动向次数最多
intend_rank 用户动向等级 用户的购买志愿等级,级数越高,动向越大
user_intend 表的数据量:800 万
第四张表:user_order(用户订单表,此处只取近六个月的订单数据,用户粒度)
字段名 字段含意 字段形容
idno 用户的身份证号 下订单的用户都是实名认证的
order_num 用户的订单次数 用户近六个月下单次数
order_amount 用户的订单总金额 用户近六个月下单总金额
user_order 表的数据量:640 万
- 需要
需要非常简单,就是将以上四张表关联组成一张大宽表,大宽表中蕴含用户的根本信息,沉闷状况,购买动向及此用户下订单状况。 - 代码
依据以上需要,咱们以 user_info 表为根底表,将其余表关联为一个宽表,代码如下:
select
a.userkey,
a.idno,
a.phone,
a.name,
b.user_active_at,
c.intend_commodity,
c.intend_rank,
d.order_num,
d.order_amount
from user_info a
left join user_active b on a.userkey = b.userkey
left join user_intend c on a.phone = c.phone
left join user_order d on a.idno = d.idno;
执行上述语句,在执行到某个 job 时工作卡在 99%:
这时咱们就应该思考呈现数据歪斜了。其实还有一种状况可能是数据歪斜,就是工作超时被杀掉,Reduce 解决的数据量微小,在做 full gc 的时候,stop the world。导致响应超时,超出默认的 600 秒,工作被杀掉。报错信息个别如下:
AttemptID:attempt_1624419433039_1569885_r_000000 Timed outafter 600 secs Container killed by the ApplicationMaster. Container killed onrequest. Exit code is 143 Container exited with a non-zero exit code 143
- 歪斜问题排查
数据歪斜大多数都是大 key 问题导致的。
如何判断是大 key 导致的问题,能够通过上面办法:
- 通过工夫判断
如果某个 reduce 的工夫比其余 reduce 工夫长的多,如下图,大部分 task 在 1 分钟之内实现,只有 r_000000 这个 task 执行 20 多分钟了还没实现。
留神:要排除两种状况:
如果每个 reduce 执行工夫差不多,都特地长,不肯定是数据歪斜导致的,可能是 reduce 设置过少导致的。
有时候,某个 task 执行的节点可能有问题,导致工作跑的特地慢。这个时候,mapreduce 的揣测执行,会重启一个工作。如果新的工作在很短时间内能实现,通常则是因为 task 执行节点问题导致的个别 task 慢。然而如果揣测执行后的 task 执行工作也特地慢,那更阐明该 task 可能会有歪斜问题。
- 通过工作 Counter 判断
Counter 会记录整个 job 以及每个 task 的统计信息。counter 的 url 个别相似:
http://bd001:8088/proxy/appli…
通过输出记录数,一般的 task counter 如下,输出的记录数是 13 亿多:
而 task=000000 的 counter 如下,其输出记录数是 230 多亿。是其余工作的 100 多倍:
- 定位 SQL 代码
- 确定工作卡住的 stage
通过 jobname 确定 stage:
个别 Hive 默认的 jobname 名称会带上 stage 阶段,如下通过 jobname 看到工作卡住的为 Stage-4:
如果 jobname 是自定义的,那可能没法通过 jobname 判断 stage。须要借助于工作日志:
找到执行特地慢的那个 task,而后 Ctrl+F 搜寻“CommonJoinOperator: JOIN struct”。Hive 在 join 的时候,会把 join 的 key 打印到日志中。如下:
上图中的要害信息是:struct<_col0:string, _col1:string, _col3:string>
这时候,须要参考该 SQL 的执行打算。通过参考执行打算,能够判定该阶段为 Stage-4 阶段:
- 确定 SQL 执行代码
确定了执行阶段,即 stage。通过执行打算,则能够判断出是执行哪段代码时呈现了歪斜。还是从此图,这个 stage 中进行连贯操作的表别名是 d:
就能够揣测出是在执行上面红框中代码时呈现了数据歪斜,因为这行的表的别名是 d:
- 解决歪斜
咱们晓得了哪段代码引起的数据歪斜,就针对这段代码查看歪斜起因,看下这段代码的表中数据是否有异样。
歪斜起因:
本文的示例数据中 user_info 和 user_order 通过身份证号关联,查看发现 user_info 表中身份证号为空的有 7000 多万,起因就是这 7000 多万数据都调配到一个 reduce 去执行,导致数据歪斜。
解决办法:
能够先把身份证号为空的去除之后再关联,最初依照 userkey 连贯,因为 userkey 全部都是有值的:
with t1 as(
select
u.userkey,
o.*
from user_info u
left join user_order o
on u.idno = o.idno
where u.idno is not null
– 是能够把 where 条件写在前面的,hive 会进行谓词下推,先执行 where 条件在执行 left join
)
select
a.userkey,
a.idno,
a.phone,
a.name,
b.user_active_at,
c.intend_commodity,
c.intend_rank,
d.order_num,
d.order_amount
from user_info a
left join user_active b on a.userkey = b.userkey
left join user_intend c on a.phone = c.phone
left join t1 d on a.userkey = d.userkey;
也能够这样,给身份证为空的数据赋个随机值,然而要留神随机值不能和表中的身份证号有反复:
select
a.userkey,
a.idno,
a.phone,
a.name,
b.user_active_at,
c.intend_commodity,
c.intend_rank,
d.order_num,
d.order_amount
from user_info a
left join user_active b on a.userkey = b.userkey
left join user_intend c on a.phone = c.phone
left join user_order d on nvl(a.idno,concat(rand(),’idnumber’)) = d.idno;
其余的解决数据歪斜的办法:
- 过滤掉脏数据
如果大 key 是无意义的脏数据,间接过滤掉。本场景中大 key 有实际意义,不能间接过滤掉。
- 数据预处理
数据做一下预处理(如下面例子,对 null 值赋一个随机值),尽量保障 join 的时候,同一个 key 对应的记录不要有太多。
- 减少 reduce 个数
如果数据中呈现了多个大 key,减少 reduce 个数,能够让这些大 key 落到同一个 reduce 的概率小很多。
配置 reduce 个数:
set mapred.reduce.tasks = 15;
- 转换为 mapjoin
如果两个表 join 的时候,一个表为小表,能够用 mapjoin 做。
配置 mapjoin:
set hive.auto.convert.join = true; 是否开启主动 mapjoin,默认是 true
set hive.mapjoin.smalltable.filesize=100000000; mapjoin 的表 size 大小
- 启用歪斜连贯优化
hive 中能够设置 hive.optimize.skewjoin 将一个 join sql 分为两个 job。同时能够设置下 hive.skewjoin.key,此参数示意 join 连贯的 key 的行数超过指定的行数,就认为该键是偏斜连贯键,就对 join 启用歪斜连贯优化。默认 key 的行数是 100000。
配置歪斜连贯优化:
set hive.optimize.skewjoin=true; 启用歪斜连贯优化
set hive.skewjoin.key=200000; 超过 20 万行就认为该键是偏斜连贯键
- 调整内存设置
实用于那些因为内存超限工作被 kill 掉的场景。通过加大内存起码能让工作跑起来,不至于被杀掉。该参数不肯定会明显降低工作执行工夫。
配置内存:
set mapreduce.reduce.memory.mb=5120; 设置 reduce 内存大小
set mapreduce.reduce.java.opts=-Xmx5000m -XX:MaxPermSize=128m;
关键词:大数据培训