MapReduce合并阶段速度过慢,如何优化提速?
我提交了一个包含9000+个Mapper和4096个Reducer的MapReduce任务。每个Mapper的输入为HDFS上约10GB(5000万行)的文件,正常情况下单个Mapper运行耗时25分钟,但总有1~10个Mapper耗时长达1小时。这类Mapper在setup()执行20分钟后才进入cleanup()阶段,其中Merging环节耗时40分钟。相关日志如下:
2018-05-19 12:03:14,791 INFO [main] org.apache.hadoop.mapred.Merger: Merging 8 sorted segments 2018-05-19 12:03:14,792 INFO [main] org.apache.hadoop.mapred.Merger: Down to the last merge-pass, with 8 segments left of total size: 1282276 bytes 2018-05-19 12:03:14,805 INFO [main] org.apache.hadoop.mapred.Merger: Merging 8 sorted segments 2018-05-19 12:03:14,810 INFO [main] org.apache.hadoop.mapred.Merger: Down to the last merge-pass, with 8 segments left of total size: 1181792 bytes 2018-05-19 12:03:14,845 INFO [main] org.apache.hadoop.mapred.Merger: Merging 8 sorted segments 2018-05-19 12:03:14,904 INFO [main] org.apache.hadoop.mapred.Merger: Down to the last merge-pass, with 8 segments left of total size: 1028459 bytes 2018-05-19 12:03:14,912 INFO [main] org.apache.hadoop.mapred.Merger: Merging 8 sorted segments 2018-05-19 12:03:14,913 INFO [main] org.apache.hadoop.mapred.Merger: Down to the last merge-pass, with 8 segments left of total size: 988898 bytes
根据你的描述和日志信息,我来帮你分析下Mapper中Merging环节过慢的原因和优化方案:
核心问题分析
从日志里能看到,每次merge的segment总数据量其实很小(都是几百KB级别),但耗时却异常长,这大概率是磁盘IO瓶颈、节点资源竞争或者Map端内存/merge配置不合理导致的。下面是具体的优化手段:
1. 调优Map端内存缓冲区,减少溢写次数
Mapper的输出会先写入内存缓冲区,达到阈值后溢写到磁盘生成sorted segment。如果缓冲区太小,会导致溢写次数增多,segment数量变多,merge时的磁盘IO开销就会剧增。
- 增大Map输出缓冲区大小:调整
mapreduce.task.io.sort.mb(默认100MB),根据节点内存情况可以调至200-400MB:<property> <name>mapreduce.task.io.sort.mb</name> <value>300</value> </property> - 提高溢写阈值:
mapreduce.map.sort.spill.percent(默认0.8),内存充足的话可以调到0.85-0.9,减少溢写次数:<property> <name>mapreduce.map.sort.spill.percent</name> <value>0.85</value> </property>
2. 优化merge并行度,减少merge pass次数
日志里每次merge的segment是8个,这个数量由mapreduce.task.io.sort.factor(默认10)控制,它决定了一次merge操作能处理的segment数量。如果节点磁盘IO能力较强,可以适当调大这个值,减少merge的总pass次数:
<property> <name>mapreduce.task.io.sort.factor</name> <value>16</value> </property>
注意:这个值不要调得过大,否则会占用过多内存,引发GC问题。
3. 排查慢节点的硬件/资源问题
那些耗时超标的Mapper,很大概率是运行在性能较差的节点上,比如:
- 磁盘IO性能差(比如磁盘使用率长期100%、存在坏道):可以用
iostat、dstat工具查看节点的磁盘读写速率和等待时间; - 节点负载过高:检查CPU、内存使用率,看是否有其他进程抢占资源;
- 临时移除这类节点,或者调整YARN的调度策略,避免把Mapper任务分配到这些节点上。
4. 启用Map端输出压缩,降低磁盘IO量
启用Map端输出压缩能大幅减少磁盘写入/读取的数据量,直接加快merge速度。推荐用Snappy压缩,它的压缩和解压速度快,CPU开销低:
<property> <name>mapreduce.map.output.compress</name> <value>true</value> </property> <property> <name>mapreduce.map.output.compress.codec</name> <value>org.apache.hadoop.io.compress.SnappyCodec</value> </property>
5. 调大Mapper的JVM堆内存,避免GC卡顿
如果Mapper的JVM内存不足,会导致频繁的垃圾回收,间接拖慢merge进程。可以根据节点内存情况调大堆内存:
<property> <name>mapreduce.map.java.opts</name> <value>-Xmx4096m</value> </property>
比如节点有8GB内存的话,给Mapper分配4GB是比较合理的配置。
6. 检查是否存在数据倾斜
虽然日志里的merge数据量不大,但如果某个Mapper处理的键值对存在严重倾斜,可能导致个别segment数据量异常大,merge时耗时增加。可以检查这些慢Mapper的输入数据,看是否存在热点键(某类键的数量远高于其他键),针对性地处理数据倾斜(比如拆分热点键、局部聚合等)。
内容的提问来源于stack exchange,提问作者whiker

