5.2.1 Map阶段调优
操作场景
Map运行阶段分为:Read、Map、Collect、Spill、Merge五个阶段。
Map任务执行会产生中间数据,但这些中间结果并没有直接IO到磁盘上,而是先存储在缓存(buffer)中,并在缓存中进行一些预排序来优化整个map的性能,存储map中间数据的缓存默认大小为512M,由“mapreduce.task.io.sort.mb”参数指定。
“mapreduce.task.io.sort.mb”大小可以根据需要调整。当map任务产生了非常大的中间数据时可以适当调大该参数,使缓存能容纳更多的map中间数据,而不至于大频率的IO磁盘,当系统性能的瓶颈在磁盘IO的速度上,可以适当的调大此参数来减少频繁的IO带来的性能障碍。
由于map任务运行时中间结果首先存储在缓存中,默认当缓存的使用量达到80%( map“reduce.map.sort.spill.percent”默认0.8)的时候就开始写入磁盘,这个过程叫做spill(也叫溢出),进行spill的缓存大小可以通过“mapreduce.map.sort.spill.percent”参数调整,这个参数可以影响spill的频率。进而可以影响IO的频率。
当map任务计算成功完成之后,如果map任务有输出,则会产生多个spill。接下来map必须将些spill进行合并,这个过程叫做merge, merge过程是并行处理spill的,每次并行多少个spill是由参数“mapreduce.task.io.sort.factor”指定的默认为64个。但是当spill的数量非常大的时候,merge一次并行运行的spill仍然为64个,这样仍然会频繁的IO处理,因此适当的调大每次并行处理的spill数有利于减少merge数因此可以影响map的性能。
如何判断该参数配小了,Job的Counters中,spill的record与Map的中间结果record数量是否一致。Spill record大于Map的输出时,说明发生了多次spill,即写磁盘操作。
“mapreduce.task.io.sort.mb”的大小,需要考虑几个因素:
- 分配给jvm的堆大小
- 用户的Map函数中的业务代码本身需要多少内存
- Map中间结果多大
Jvm堆大小是在用户执行任务时候指定的(mapreduce.map.java.opts)。Map的中间结果大小可以通过Job运行结束后的JobCounters中找到。同样,用户的业务代码使用了多少内存可以在JobCounters中找到Total committed heap usage (bytes)来估算(约等于Total committed heap usage减去Map buffer的大小)。
计算出一个合理的buffer大小,可以用一个简单的公式:
Buffer的大小=jvm堆大小-业务代码占用量-200M
预留200M的意思是给其他可能的内存占用提供空间,避免jvm频繁gc。
操作步骤
参数名 | 描述 |
---|---|
mapreduce.task.io.sort.mb客户端参数 | I/O排序内存缓冲,默认512M。 |
mapreduce.task.io.sort.factor客户端参数 | I/O排序因子,默认为64。文件排序时可一次合并的流。 |
mapreduce.map.sort.spill.percent客户端参数 | 默认值0.8,磁盘IO是主要瓶颈,合理配置“mapreduce.task.io.sort.mb”可以使溢出至磁盘的内容最小化。如果map在内存中sort的结果达到一个特定的值,就会被spill进入硬盘。具体这个值是等于mapreduce.task.io.sort.mb * mapreduce.map.sort.spill.percent.具体参见操作场景描述。 |
mapreduce.reduce.shuffle.parallelcopies客户端参数 | 默认值10,洗牌期间并行传输的默认数量,调大需要考虑造成网络的拥堵。 |
mapreduce.shuffle.max.connections服务端参数 | 最大洗牌连接。 |