本文记录了Hive任务出现OOM的排查经过…
1 背景
为节约存储成本,我们在产品侧帮助用户将Text格式表更改为Orc格式,但在修改之后,用户原有离线任务出现OOM异常:
1 | Error: GC overhead limit exceeded |
1 | Error: Java heap space |
1 | Exception from container-launch. Container id: container_e10_1582793079899_23336729_01_001009 Exit code: 255 Stack trace: ExitCodeException exitCode=255: at org.apache.hadoop.util.Shell.runCommand(Shell.java:545) at org.apache.hadoop.util.Shell.run(Shell.java:456) at org.apache.hadoop.util.Shell$ShellCommandExecutor.execute(Shell.java:722) at org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor.launchContainer(LinuxContainerExecutor.java:372) at org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.call(ContainerLaunch.java:306) at org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.call(ContainerLaunch.java:85) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Shell output: main : command provided 1 main : user is yarn main : requested yarn user is dj_dw Container exited with a non-zero exit code 255. Last 4096 bytes of stderr : SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/home/yarn/nodemanager-hadoop-2.7.2-4901/share/hadoop/common/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/data7/hadoopdata/nodemanager/local/usercache/dj_dw/filecache/5953/udf_sec_data_masking.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory] log4j:WARN No appenders could be found for logger (org.apache.hadoop.yarn.YarnUncaughtExceptionHandler). log4j:WARN Please initialize the log4j system properly. log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. Halting due to Out Of Memory Error... |
2 排查经过
2.1 定位到异常栈
首先我们需要定位到异常栈,看看到底为什么OOM。如下所示,在reduce阶段:
1 | 2020-04-04 22:00:49,755 INFO [communication thread] org.apache.hadoop.mapred.Task: Communication exception: java.lang.OutOfMemoryError: GC overhead limit exceeded |
都明白是OOM了,该怎么办呢?如何调整MR参数呢?
2.2 先一起来看看MR任务资源相关的参数
1)MapReduce任务资源相关参数:
*mapreduce.map.memory.mb: **
一个 Map Task 可使用的内存上限(单位:MB),默认为 1024。如果 Map Task 实际使用的资源量超过该值,则会被强制杀死。*mapreduce.reduce.memory.mb: **
一个 Reduce Task 可使用的资源上限(单位:MB),默认为 1024。如果 Reduce Task 实际使用的资源量超过该值,则会被强制杀死。*mapreduce.map.cpu.vcores: **
每个 Maptask 可用的最多 cpu core 数目, 默认值: 1*mapreduce.reduce.cpu.vcores: **
每个 Reducetask 可用最多 cpu core 数目默认值: 1*mapreduce.map.java.opts: **
Map Task 的 JVM 参数,你可以在此配置默认的 java heap size 等参数, 例如:“-Xmx1024m -verbose:gc -Xloggc:/tmp/@taskid@.gc” (@taskid@会被 Hadoop 框架自动换为相应的 taskid), 默认值: “”*mapreduce.reduce.java.opts: **
Reduce Task 的 JVM 参数,你可以在此配置默认的 java heap size 等参数, 例如:“-Xmx1024m -verbose:gc -Xloggc:/tmp/@taskid@.gc”, 默认值: “”2)yarn资源调度相关参数:(需要在 yarn 启动之前就配置在服务器的配置文件中才能生效)
yarn.scheduler.minimum-allocation-mb:*
RM 中每个容器请求的最小配置,以 MB 为单位,默认 1024。
yarn.scheduler.maximum-allocation-mb:
RM 中每个容器请求的最大分配,以 MB 为单位,默认 8192。
yarn.scheduler.minimum-allocation-vcores:
默认值1
yarn.scheduler.maximum-allocation-vcores:
默认值 32
yarn.nodemanager.resource.memory-mb:
表示该节点上YARN可使用的物理内存总量,默认是 8192(MB),注意,如果你的节点内存资源不够 8GB,则需要调减小这个值,而 YARN不会智能的探测节点的物理内存总量。
3)shuffle 性能优化的关键参数:(需要在 yarn 启动之前就配置好)
mapreduce.task.io.sort.mb:*
100 shuffle 的环形缓冲区大小,默认 100m
mapreduce.map.sort.spill.percent:*
0.8 环形缓冲区溢出的阈值,默认 80%
2.3 分析调优
再看下异常栈,是在getProcessList
时OOM:
1 | 2020-04-04 22:00:49,755 INFO [communication thread] org.apache.hadoop.mapred.Task: Communication exception: java.lang.OutOfMemoryError: GC overhead limit exceeded |
这里在干什么呢?找到源码位置:
1 | private List<String> getProcessList() { |
最后OOM位置是groups = new int[parentGroupCount * 2];
至此,你应该能想到应该调整JVM参数。查看reduce的jvm默认参数:
1 | mapreduce.reduce.java.opts -Xmx1024m |
调整为2048,问题解决。
1 | set mapreduce.reduce.java.opts=-Xmx1024m |
3 总结
通常遇到MR调优,往往用户盲目调整参数,没有找到根因。另外,要善于抓住异常栈,习惯从源码入手(源码不可怕,可怕的是你打开源码的勇气)。
本文链接: https://stefanxiepj.github.io/archives/b4b5c81c.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!
![知识共享许可协议](https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png)