分布式系统MaxCompute/Hadoop日志分析与优化流程

2 篇文章 0 订阅
2 篇文章 0 订阅

前言

很久没有认真复盘一下,接着两年前的文章Hadoop性能优化概述-数据膨胀&数据倾斜系统性总结一下优化方案和如何从日志中分析性能卡点,同时方便自己加深印象0.0~
我们在提交作业后,一般会分为4各阶段:
(1)预处理阶段;
(2)编译阶段;
(3)执行阶段;
(4)结果返回。
其中,预处理阶段除了语法、调度配置与参数配置等出现问题外,一般不会成为卡点。我们重点关注复杂任务的编译、执行阶段的优化以及性能卡点。

1.0 编译阶段

编译阶段包括调度、任务优化、物理执行计划。

1.1 调度

很多时候我发现自己提交完任务后,跑了很久,此时会直接去暂停任务,看看哪里写错了,其实标准的流程应该是从调度日志来看。很多时候会发现其实我们的任务并没有真正跑起来。
1. 集群计算资源紧张。
这个其实是最常见的一种情况,一般提交任务会选择某个集群,我们首先先要预估一下自己的作业大概会耗费多大计算资源,然后选择资源充足的队列。
其次,如果这个队列使用的人过多,出现排队的情况,也会导致上述问题,你的作业其实一直都在waiting。此时应当考虑(代码无优化空间的前提0.0…)是否换空闲的队列,或者提高自己任务的优先级,当然如果你有管理权限,也可以粗暴的杀掉某些不合理的任务(提前和那个owner沟通好,不然会打架吼吼…~)
2. 编译池资源不足。
可能有人不小心提交了太多的作业(不小心勾错了…)这个时候及时沟通,杀掉!!
3. 上游任务无产出。
很多时候生产环境的周期性任务,我们发现很久没有产出或者跑起来,很有可能是你任务依赖的上游产出不稳定。这个时候,需要评估下你的上游是否能准时产出,如果不能,业务上无影响的情况可以使用弱依赖max_pt。

1.2 优化阶段

优化器耗时过长。
一般就是你的任务过于复杂,还没有完成任务优化。好像有些firm的onemodel没有这个功能?不管怎样,等了10几分钟还没调起来,杀掉吧,看看把复杂任务通过数据模型设计,降低复杂度。

1.3 物理执行计划

通俗讲分区过多,就会影响物理计划split数据切片的生成效率。

  1. 分区筛选。
    举个例子,对于全量表只筛选t+1分区,对于增量表需要限定合理的分区数量。当然有些人会忘记选择分区,一般也会触发严格模式strict mode提交的过程就会报错。
  2. 设计合理的作业流程
    很多时候没有必要把一大批作业都放到一个任务里,举个例子:我现在想要做一张全量表。
    其实可以手动任务补1年数据,在全量表基础上,每日union增量表再去去重,这样你不会每次都读取n多分区造成任务冗余。
  3. 生成的小文件过多
    看一下我的错误示范0.0~~

常见的就是,插入分区过多。比如动态分区中,尤其是多级分区,每一个上报时间日期戳都创建一个子分区(其实就是创建n多个小文件)或者一个任务同时插n多个分区,就会导致如此。

2.0 执行阶段

拿自己一个任务做个详解(脱敏)→_→
在这里插入图片描述
其实主要关注的就是哪一个map/reduce阶段一直running, 耗时很严重,数据输入/输出内存过大,远远超过预期。
常见的情况分为:等待资源、shuffle过大、map阶段split的切片过多、数据倾斜、数据膨胀、UDF执行效率过低。

2.1 等待资源

当我们发现部分instance实例处于ready或者running状态,其实就是在等待资源分配。这个时候可以查看一下排队情况,比如在你前面有大资源消耗任务,或者人很多,需要合理选择队列、要么杀掉浪费资源的任务、或者提高自己任务的优先级。(自己代码无优化空间前提)。
当然,这个时候也是一个机会,观察集群性能消耗一定时间,可以申请扩容。

2.2 shuffle阶段过大

简单总结下shuffle是什么。
在这里插入图片描述

map输出到reduce输入的过程称为shuffle阶段(洗牌)。这个阶段会根据分区策略将同一分区的数据分配到同一reduce中。
map的输出是k,v对,先进行combine将相同的key合并在一起。 通过hash(key)mod(reduce数目)
计算出partition的ID,上述的key就被分配给这个partition;一个partition中可以有多个key,但是同一个key只存在于一个partition中;一个partition对应一个reduce。有的partition负载可能很重,有的则很轻,需要通过一定的协调机制平衡负载(比如根据自己的需求重写partition函数)。

举个例子,我们map阶段拆出了很多切片数据(简单讲就是你数据量太大了),这个时候在传输到reduce端这个过程,就会十分耗时。一个简单的例子:

massive data
left outer join
standard data on key..

这个massive的数据量可能千亿级别,分布十分散。那么你正常的数据量可能只有几千万,这个时候在copy写入reduce端磁盘的过程就会十分耗费资源。
有几个小的优化的方法:
a. union all + row_number() 窗口函数去重

select * from
(select
row_number() over (partition by 主键 order by ?? desc) as rn
from
(全量表
union all
增量表)
where
rn = 1) a
left outer join
(
standard data
) b on key...

b. left anti join 去重

select * from
(select
??
from
(全量表)
left anti join
(增量表)
union all
增量表) a
left outer join
(
standard data
) b on key...

c.通过分桶
一般mapreduce计算的都是海量数据,map输出时候不可能把所有文件都放到内存操作,因此map写入磁盘的过程十分的复杂,更何况map输出时候要对结果进行排序,内存开销是很大的,map在做输出时候会在内存里开启一个环形内存缓冲区,这个缓冲区专门用来输出的,默认大小是100mb,并且在配置文件里为这个缓冲区设定了一个阀值,所以可能会被撑满。
具体解决方法,可以参考“clustered by”。

2.3 map阶段split的切片过多

这个从日志里可以很好观察到,值得提一嘴的是…有一些set mapper split size 的参数设置,虽然可以提速,但是如果你拆了过多的小文件,这个过程反而更耗时,所以在开发阶段的时候就要评估下具体如何设置这个参数阈值。

2.4 数据倾斜

倾斜主要发生在Reduce阶段,而很少发生在 Map阶段,我们上述讲到了map阶段会均匀拆分数据到不同块,而这个过程如果倾斜往往是因为hdfs的内存不够问题,很少见。
而Reduce阶段的数据倾斜几乎都是因为数据研发工程师没有考虑到某种key值数据量偏多的情况而导致的。像我们上述降到,会把相同的key放在一个partition传送到reduce, 那比如reduce_1的数据量远远大于reduce_2的时候,就会出现倾斜。卡在这里。

先来看一下常见的数据倾斜语句:
在这里插入图片描述
!原理就是上述谈到的那些,分配资源不均,我记得好像分配reduce资源的时候是按照不同分桶组合的平均值来分的,所以倾斜才会导致整体处理能力不足。

!还有一个方法,我们在看mapreduce task 和拆分的instance时候,某个instance 的计算耗时明显大于平均的3倍,那么铁定就倾斜了。

日志中我们可以看到:task有N多个实例Instance都结束啦,但是个别几个还在running迟迟不结束(长尾)。还有在日志中看到某个reducer中开启了backup备用实例,也可能是倾斜。
我们需要定位长尾的实例,具体问题具体解决。
列举几个常用解决方案:
1. 设置参数

-- map端的Combiner,默认为ture
set hive.map.aggr=true-- 开启负载均衡
set hive.groupby.skewindata=true (默认为false

2. join倾斜优化

a. 热点值散列处理

(??) a
join
(??) b on 
case when coalesce(a.device_id,'') in ('','-') then concat('xx',rand()-1) else a.device_id = b.device_id

b. mapjoin
小表join大表,key都集中在小表。
将小表table_a,别名为ta的表放到map端的内存中
如果想将多个表放到Map端内存中,只需在mapjoin()中写多个表名称即可,用逗号分隔,如将a表和c表放到Map端内存中,则 /* +mapjoin(a,c) */

select
/*+ mapjoin(ta)*/
tt_id,
pp_good
from
table_a ta
left join table_b tb on ta.tt_id = tb.tt_id;

另外一点~ mapjoin 可以做不等式的join哟~😄

还有一种sort_merge_bucket join 也可以,没有用过就不讲了。感兴趣可以查查。

c. 把筛选条件尽量写在on上
这个事儿我之前不知道,后来听大佬讲过,不要把筛选条件写在外层,尽量写在On上就会快很多,这个算是优化提速,不一定是倾斜。

3. group by倾斜优化
上线的set hive.groupby.skewindata=true其实可以解决一部分。

还有一种方法,其实就是拆分多层去进行聚合union all在一起,不要一股脑直接按照key1 key2 key3去聚合。这里就不多说了。
4. multi distinct倾斜优化
倾斜案例:

select
city,
count(distinct uid) as uid_cnt,
count(distinct device_id) as device_cnt,
count(distinct ip) as ip_cnt
from
table_content
where
dt_par = '???'
group by
city

我们发现一连串count(disitnct) 会导致倾斜,加入某一列空值很多。。。
所以我们一般尽量不要count(distinct) ,我们有两种方法替代:
(1) 双层or多层group by

先聚合多个维度,再在外层聚合最终结果。

(2) with as …group by

with tmp as (
SELECT  city
        ,uid
        ,device_id
        ,ip
FROM    table_content
WHERE   dt_par = '???'
GROUP BY city
         ,uid
         ,device_id
         ,ip
)

SELECT  city, count(uid) AS uid_cnt
        ,count(device_id) AS device_cnt
        ,count(ip) AS ip_cnt
FROM    tmp
GROUP BY city
;

2.5 数据膨胀

数据膨胀: task输出的数据量比输入大很多很多。
举个例子,比如两表连接之后,数据量远超两表之和,可能是笛卡尔积导致,因为一个主键对应多条记录。
当然还可能是,写错连接条件。(没写会触发严格模式,尽量不要取消严格模式参数non-district)
从日志上看:StdOut 里一直在打印 Merge join 的日志,说明对应的单个 worker 一直执行 merge join,Merge join 输出的数据条数已经远超预期的条数,就可能有严重的数据膨胀,需要检查 JOIN 条件和 JOIN key 是否合理。
在分析一个上层指标时,连接了N张表,越垒越大,这种情况就是时候构建中间层,构建数据Cube,先行聚合,在聚合的基础上再聚合,这样既可以复用中间钻取的数据表,减少了数据量,又加速了关联查询。
此外很重要的一点,要对你的数据情况极其敏感和了解,引起膨胀的可能因素就是你的某个子查询中出现,性别、时间戳或者某些分类的影响,常发生于join。

2.6 UDF执行效率低

UDF用户自定义函数,广泛意义上的。从日志中可以看到,某个task执行效率极低,其中是自定义函数扩展的报错。

  1. 自定义函数执行超时
    设置合理的超时时间。
  2. 用内置函数替换自定义函数,或者拆解udf中部分替换成内置函数,更可靠。
  3. evaluate方法会反复执行。
    之前遇到过某个udf是调了http接口,反复批量调执行evaluate方法,几千万的数据,每次只能查8个,这样肯定慢。。。这种任务就不要写udf了。
  4. 内存溢出。
    这块可以设置合理的gc管理,调整内存参数。

3.0 结束阶段

结束阶段就是,你发现所有的task都已经关了,但是仍在running,出现这种情况大概率是结果合并、传输、存储的问题。

  1. 小文件过多导致。
    分布式系统处理大文件的效率远远高于小文件,当小文件(默认小于32MB)过多,会触发自动merge,日志里看就是出现了类似 "XXXXjob_merge”,此时需要关注下减少小文件。(拆开放到多个任务!)
  2. 下载远端传输
    有时候跑Python、pyspark任务需要服务器远程传输下载,这个过程也慢。或者即席查询,传输到云端下发到本地前端也需要时间。
  3. 动态分区过多
    详见本文“1.3.3"。

*结束语

持续更新中…
很多时候,不用就忘,还是没事多写一写。
希望大佬可以多多指正,一起进步~!
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值