MapReduce:计算Job运行时的CPU与内存平均利用率

本文介绍了一种在Hadoop集群中监控MapReduce Job资源使用情况的方法。通过在任务的setup阶段启动资源收集器线程,收集子JVM的CPU和内存使用情况,并使用MapReduce框架的Counter机制进行记录。最后分析收集到的数据,以评估不同类别的Job对集群资源的实际消耗。
[size=medium]Hadoop集群上运行有多道MapReduce Job,到底每个Job消费集群计算资源(CPU,内存)的情况如何,通常我们是不知道的。这节来探讨下如果想获得这些信息,应该怎样做。

每个Job在运行时是散布在TaskTracker上,由不同的JVM来执行。所以这个问题归根结底就是想获取每个运行task的JVM对资源的消费情况。对于这种获取分布式环境数据的例子,就可以采用MapReduce框架自带的Counter机制,由它来采集各JVM运行期的实时数据,然后在Job结束后,做最终分析。

[b]这项任务的意义在哪里?[/b]
正常情况下,Job会因为数据范围、服务的应用、数据量的大小及MapReduce逻辑的复杂程度等因素而被定义为不同的类别。当类别的概念形成后,我们就想知道每个类别Job对集群资源的使用情况是否符合预期,如果可以获得这些数据,就可以根据资源使用情况对Job的执行时间做相应的调整,以避免对其它Job的影响。

[b]数据采集什么时候开始与结束?[/b]
我们的目标是分析每个子JVM的资源消耗情况,就应该在子JVM开始运行时介入收集。站在MapReduce框架外,只有一个地方可以作为介入点:Mapper和Reducer的setup方法。它的参数是task运行期Context,可以很方便地调用Counter。那我们的入口就可以像这样[/size]


public class ResourceCollectMapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
extends Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {

ChildJVMResourceCollector collector = null;
public void setup(Context context) {
collector = new ChildJVMResourceCollector(context);
collector.start();
}

}


[size=medium]资源收集器是一个独立线程,在setup方法中启动,在cleanup方法调用时结束。本来整个task的执行过程就包含在setup与cleanup之间,所以我们完全有机会收集这个过程中的资源利用情况。

[b]如何获取运行task的JVM pid?[/b]
运行task的子JVM是由TaskTracker创建的,对用户完全透明。但幸运的是,TaskTrack在创建这个子JVM时,把它的pid作为参数传给了子JVM。获取的方式如下[/size]

private String getChildJVMPid() {
String pid = "";
if (!Shell.WINDOWS) {
pid = System.getenv().get("JVM_PID");
}
return pid;
}


[size=medium][b]怎样根据pid获取系统级的资源使用率?[/b]
得到当前子JVM的进程号后,就能用这个进程号查询Linux系统当前进程的资源使用情况。查询的方式网上很多资料都有说明[/size]

[img]http://dl.iteye.com/upload/attachment/558968/7a01b303-084a-36f4-a89a-b59a052daa9e.jpg[/img]

[size=medium]上图是通过ps命令查询某个进程的CPU与内存使用情况的截图。其它相似的查询方式大家可以自行实验
如果采用上述的linux命令,就需要分开组装,并调用Hadoop提供的Shell组件。[/size]

String[] queryCommand = new String[]{"ps", "-o", "%cpu,%mem", pid};
String result = Shell.execCommand(queryCommand);

[size=medium]返回的查询结果是一个字符串,解析字符串,拿到想要的结果,以特定的key把结果存入Counter中(当然获取次数也需要收集,在Counter中由独立key标识),这样我们的一次数据获取过程就结束了。

[b]最终怎样分析?[/b]
在Job结束后,根据特定的key获取相应的数据,然后除以获取次数,就是平均的CPU与内存使用率。

[b]要注意的情况是?[/b]
在这个过程中需要注意有两个地方:
1. 尽量让获取资源的线程不要占用太多的执行时间,每隔一段时间启用一次,减少对正常task执行的影响;
2. 如果遇到某个Job的多个task共用一个子JVM的情况(MapReduce优化的一点,减少频繁创建/销毁JVM的代价),就需要确保在当前task的cleanup方法中一定让获取资源的线程终结掉。这个线程包含着task运行期的Context,如果是Map task的话,那个默认100MB的内存缓冲区也在其中,可能会有OOM问题而影响下一个task的创建过程。[/size]
# MapReduce计算慢的深度分析及优化方案 ## 一、核心性能瓶颈分析 ### 1. Shuffle阶段瓶颈 - **数据倾斜**:少数Reducer处理大量数据(80/20现象) - **网络IO**:跨节点传输数据量大(平均Shuffle数据量=输入数据×膨胀系数) - **磁盘IO**:中间结果溢写频繁(默认阈值100MB) ### 2. Map阶段低效 - **GC间长**:平均GC间超过任务间的15% - **CPU利用率低**:平均低于50%的集群利用率 - **数据本地性差**:>30%的任务需要跨节点读数据 ### 3. Reduce阶段问题 - **启动过早**:Map完成前就开始拉取数据(mapreduce.reduce.slowstart.completed.maps默认0.05) - **内存不足**:频繁触发spill到磁盘(reduce.shuffle.input.buffer.percent默认0.7) ## 二、关键优化技术 ### 1. 数据倾斜解决方案 ```java // 采样+二次分发方案 Job job = Job.getInstance(conf); // 先运行采样job确定Key分布 Sampler<Text, Text> sampler = new RandomSampler<>(0.1, 10000); InputSampler.writePartitionFile(job, sampler); // 主Job使用定制分区 job.setPartitionerClass(TotalOrderPartitioner.class); ``` ### 2. Shuffle优化参数 ```xml <!-- yarn-site.xml --> <property> <name>mapreduce.reduce.memory.mb</name> <value>4096</value> <!-- 建议为Container内存的70% --> </property> <property> <name>mapreduce.task.io.sort.mb</name> <value>512</value> <!-- 不超过JVM堆的50% --> </property> ``` ### 3. 计算模式优化 | 模式 | 适用场景 | 配置示例 | |------|----------|----------| | Map-only | 无聚合需求 | setNumReduceTasks(0) | | Combiner | 可本地聚合 | job.setCombinerClass(MyReducer.class) | | In-Mapper | 小数据聚合 | 在map()中维护HashMap | ## 三、性能对比指标 优化前 | 优化后 | 提升幅度 ---|---|--- Shuffle数据量10GB | Shuffle数据量2GB | 5x Reducer执行间300s | Reducer执行间60s | 5x 任务总长600s | 任务总长180s | 3.3x ## 四、高级优化方案 ### 1. 基于Tez的DAG优化 ```java // 构建DAG执行计划 DAG dag = new DAG("WordCount"); Vertex mapVertex = new Vertex("map", MapProcessor.class); Vertex reduceVertex = new Vertex("reduce", ReduceProcessor.class); dag.addVertex(mapVertex).addVertex(reduceVertex); dag.addEdge(new Edge(mapVertex, reduceVertex)); ``` ### 2. 列式存储优化 ```sql -- ORC文件格式+谓词下推 CREATE TABLE optimized_table ( user_id STRING, cnt INT ) STORED AS ORC tblproperties ("orc.compress"="SNAPPY"); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值