前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MR调优实战

MR调优实战

原创
作者头像
程序猿
发布2018-08-12 21:53:53
2.2K0
发布2018-08-12 21:53:53
举报

本文将从资源分配和Shuffle调优两方面进行调优。

一、资源分配:

在调优资源分配之前,需要先了解一下yarn的资源分配流程。

如何给一个MR任务分配资源将大大影响其运行性能。常用的资源参数有:

  • ① yarn.nodemanager.resource.memory-mb:nodemanager内存资源总大小,节点能够使用的最大内存。可以在yarn的web ui中的activenodes里面看到的每个节点的内存总大小就是这个值。该值需要小于实际的机器物理内存。
  • ② yarn.scheduler.minimum-allocation-mb:单个container申请的最小内存,如果是容量调度or先进先出,该参数还是规整化因子(公平调度规整化因子是yarn.scheduler.increment-allocation-mb)。
  • ③ yarn.scheduler.maximum-allocation-mb:单个container申请的最大内存
  • ④ yarn.nodemanager.pmem-check-enabled:是否开启物理内存监控。如果关闭,任务在实际运行过程中可以使用最大限度的内存,如果有大任务,很可能会占用完整个机器的内存,导致卡死。不建议关闭。
  • ⑤ yarn.nodemanager.vmem-check-enabled:是否开启虚拟内存监控(虚拟空间就是利用磁盘空间 逻辑上充当 物理内存,内存变大了,但是虚拟内存访问是比较物理内存慢的)。
  • ⑥ yarn.nodemanager.vmem-pmem-ratio:虚拟内存 - 物理内存比。
  • ⑦ mapreduce.map.memory.mb:map任务占用内存。
  • ⑧ mapreduce.reduce.memory.mb:reduce任务占用的内存。一般这是为2倍mapreduce.map.memory.mb
  • ⑨ mapreduce.map.java.opts:map任务中,启动的处理任务中的jvm参数。一般设置堆栈值,gc参数等。比如-Xmx2048m。这里需要说明一下,启动的处理任务的堆栈大小默认是任务内存的80%
  • ⑩ mapreduce.reduce.java.opts:同mapreduce.map.java.opts,只不过是reduce任务。
  • ⑪ mapred.max.split.size:map任务处理的split最大值。当map读取大文件时,会将大文件按照mapred.max.split.size并以blocksize为单位切分。比如该值设为1G,当处理2.3G的大文件时,会生成两个1G的split,并产生对应的两个map,剩下0.3G暂时保留(0.3G还要和min做比较)。
  • ⑫ mapred.min.split.size.per.node:每个节点的最小split大小,节点先会按照max split进行切分,然后把剩余不足max的数据,再和min比较,如果大于min,则为其单独生成map处理。否则保留。
  • ⑬ mapred.min.split.size.per.rack:由于max和min split设置的原因,每个node可能会有保留的小数据未分配map处理。此时会将相同rack的node的保留数据进行合并,如果小于该值,则保留,否则合并成一块数据,为其创建map处理。(经过rack合并之后,如果还有保留的数据,最后将全部合并,为其创建map处理。)

参数1-6是针对yarn的设置。参数1是告诉集群本节点有多少内存资源。2和3设置单个container能够申请到的最小最大内存。4是是否物理开启内存监控,监控container实际占用内存是否超标,超标则直接kill掉。5和6分别设置虚拟内存监控开关以及虚拟内存大小。

参数7-10是设置mr内存的,oom了可以调大,想提高并发,可以调小。11-13是设置map处理的split大小的,单个map运行时间才几十秒,说明这个map处理的数据比较小,可以增大split,让单个map处理数据更大,运行更长,当热因为单个map处理的数据变大了,总的map数就降低了。

注意:7-10中的参数中,设置的jvm对大小一定要小于对应任务分配的内存。

  • mapreduce.map.memory.mb > mapreduce.map.java.opts
  • mapreduce.reduce.memory.mb > mapreduce.reduce.java.opts

原因:以map为例,map任务执行中,除开开启java进程处理程序外,还有额外的一些工作需要内存。换而言之:mapreduce.map.java.opts + 其他 = mapreduce.map.memory.mb。如果设置的mapreduce.map.java.opts中的最大内存>=mapreduce.map.memory.mb了,说明允许堆栈增长内存大于mapreduce.map.memory.mb,一般而言是会开启yarn.nodemanager.pmem-check-enabled的,当发现总的内存值大于了mapreduce.map.memory.mb,就会kill掉该map,报错oom。报错信息:

代码语言:javascript
复制
Current usage: 4.4 GB of 4 GB physical memory used; 12.2 GB of 32 GB virtual memory used. Killing container

二、shuffle调优:

想要对shuffle操作进行调优,首先的了解shuffle的流程。

在了解了MR shuffle流程之后,只需要对shuffle流程中各个环节的参数进行适当配置,就能有效加速shuffle的执行过程。shuffle过程中常用性能参数:

  • ① mapreduce.task.io.sort.mb:map任务在shuffle_write阶段的buffer大小,默认100M。
  • ② mapreduce.map.sort.spill.percent:map任务在shuffle_write阶段,当写入buffer达到一定比例会进行spill溢写。默认0.8。
  • ③ mapreduce.task.io.sort.factor:map任务shuffle_write阶段,可能会溢写很多的文件,势必会产生很多小文件,这个时候就需要合并这些小文件。该参数就规定每次合并小文件的个数。
  • ④ mapreduce.map.combine.minspills:在合并spill文件时,如果设置了combiner同时spill文件数大于该值,在合并过程中还会触发combiner,从而减少最终输出的文件大小。
  • ⑤ mapreduce.reduce.shuffle.parallelcopies:在reduce的shuffle_read阶段,会从上游map任务copy数据。该值是copy操作的线程数,默认5。
  • ⑥ mapreduce.reduce.shuffle.input.buffer.percent:reduce的shuffle_read阶段,copy的数据都会放在缓冲区中。该值设置了buffer占用的内存比例,默认0.7。
  • ⑦ mapreduce.reduce.shuffle.merge.percent:reduce的copy缓冲区达到一定比例,将会溢写本地磁盘。默认0.66

三、调优实战(一次帮助用户调优的经历)

用户场景是离线计算一些报表数据,通过hive启动mr任务计算存放于cos上的数据。其中有一条sql计算的数据量大概有1T左右。期初不经过任何调优的情况下,用户任务执行时间长达一下午,无论如何这都是无法容忍的,于是用户希望我们能够帮助进行性能上的调优。用户的集群统一由16核-32G的CVM,共计17台组成(其中 core 12台,master 2台,common 3台, task 0台,执行计算任务的就是其中的12台core和0台task)。

先来说说其中某些现象:

  1. 通过yarn web ui查看activenodes的资源情况,发现每个节点上yarn的最大使用内存是28.8G,vcore是16核,可以猜到yarn.nodemanager.resource.memory-mb值就是28.8G(自行换算成MB单位)。这个值没有设置成机器的最大内存数,是正确的,防止占用全部机器内存,导致机器内存负载过高出现卡死的现象。
  2. 任务执行中,发现acivenodes的资源情况中,每个node的内存都剩余有8G多的内存。
  3. 任务执行中,发现map数量为4000+,reduce数量为1000+
  4. 任务执行中,发现同时为running状态map数只有8左右,reduce数量也只有8左右。
  5. 任务执行中,查看已经完成的map用时,普遍在50s-1min之间。

分析:

  1. 现象2说明集群资源利用不够充分,有剩余资源无法分配。需要想办法分配该部分剩余资源
  2. 现象3、现象4和现象5说明,4000个map,map并发8,每个map大概1分钟(分配资源给map执行时,会有额外时间开销),来算算执行完所有map的用时4000/8 * 1min = 500分钟,尼玛!这TM不止一下午,是要一天的节奏啊。问题有以下几点:
  • map并发太低了。这里就会有疑问,为什么有剩余的内存资源,不分配给map执行呢?答:我看了map任务的内存分配是10G。对,没错,是10G,晕死!reduce分配的20G,这个可以理解,reduce内存一般是map的两倍,但尼玛也太高了吧。现象2里面每个节点剩余8G内存,要想分配一个map来执行,当然分配不出来啦,所以通通给我pending住,只准同步运行可怜的几个map。说到这,解决办法大家可能想到了,没错!就是降低map和reduce的内存分配!从而让集群可以同时运行更多的任务。那到底并发数多少最理想呢?当然是vcore的2-3倍(假设一个任务只占用1核),可能会想问,为什么是2-3倍呢?你想想任务并发等于vcore是最理想的,但是哪个任务没有点IO等待的情况,等IO的时间就可以切换另一个任务来执行卅。当然太多也不好,频繁切换任务的话,因为切换本身是有时间损耗的。所以就2-3倍了!
  • 每个map才执行1分钟,分配资源给map执行的过程都需要几秒钟,额外时间消耗占比太高了(假设任务执行只需要55秒,但是为了执行这个map,要分配资源给它,中间过程就需要5S)。一般会把任务的执行时间控制在5分钟左右。过长说明要么任务有问题,要么单个任务执行的数据太多了,不合理。
  • map总数量过高。可以通过设置split来降低map总数,这样为map分配资源的次数就会减少,分配过程的时间就节约了。

经过一定量的分析,大概知道了需要调整哪些参数来提高任务运行速度。于是帮用户设置了一些参数来运行任务。之前运行需要4-5小时的任务,最后浓缩到了30分钟。

调整参数:

mapreduce.reduce.java.opts=-Xmx6600m; mapreduce.map.java.opts=-Xmx2500m; mapreduce.reduce.memory.mb=6144; mapreduce.map.memory.mb=3072; mapreduce.task.io.sort.mb=512; // 这个参数是为了让溢写的时候少点IO次数,其实还可以调大 mapred.max.split.size=1024000000; mapred.min.split.size.per.node=1024000000; mapred.min.split.size.per.rack=1024000000;

这里设置每个map处理的split最大大小是1G,但是我设置的map堆栈有2G+,原因很简单,处理输入是1G,万一代码中还有各种变化,中间数据之类的,1G堆栈肯定不够啊,反正内存够用,多来点呗,不然OOM了就悲剧了。

其实上面的参数应该还可以调整以提高速度,同时还有一些GC方面的优化没有加进去(一般最后才会想到GC优化),还有代码优化也是重要的环节之一。

文中可能会有一些地方说的不清楚,或者说的不正确的地方,希望大家指点一二,谢谢!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云 BI
腾讯云 BI(Business Intelligence,BI)提供从数据源接入、数据建模到数据可视化分析全流程的BI能力,帮助经营者快速获取决策数据依据。系统采用敏捷自助式设计,使用者仅需通过简单拖拽即可完成原本复杂的报表开发过程,并支持报表的分享、推送等企业协作场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档