【发布时间】:2014-04-27 06:28:25
【问题描述】:
我是 MapReduce 编程的初学者,编写了以下 Java 程序,用于在包含 1 个 NameNode 和 3 个 DatanNode 的 Hadoop 集群中运行:
package trial;
import java.io.IOException;
import java.util.*;
import java.lang.Iterable;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.*;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.util.*;
public class Trial
{
public static class MapA extends MapReduceBase implements Mapper<LongWritable, Text, Text, Text>
{
public void map(LongWritable key, Text value, OutputCollector<Text,Text> output, Reporter reporter) throws IOException
{
String[] rows = value.toString().split("\r?\n");
for(int i=0;i<rows.length;i++)
{
String[] cols = rows[i].toString().split(",");
String v=cols[0];
for(int j=1;j<cols.length;j++)
{
String k =j+","+cols[j];
output.collect(new Text(k),new Text(v));
}
}
}
}
public static class ReduceA extends MapReduceBase implements Reducer<Text, Text, Text, Text>
{
public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text>output, Reporter reporter) throws IOException
{
int count =0;
String[] attr = key.toString().split(",");
List<String> list = new ArrayList<String>();
while(values.hasNext())
{
list.add((values.next()).toString());
count++;
}
String v=Integer.toString(count);
for(String s:list)
{
output.collect(new Text(s),new Text(v));
}
}
}
public static void main(String[] args) throws IOException
{
JobConf conf1 = new JobConf(Trial.class);
conf1.setJobName("Trial");
conf1.setOutputKeyClass(Text.class);
conf1.setOutputValueClass(Text.class);
conf1.setMapperClass(MapA.class);
//conf.setCombinerClass(Combine.class);
conf1.setReducerClass(ReduceA.class);
conf1.setInputFormat(TextInputFormat.class);
conf1.setOutputFormat(TextOutputFormat.class);
FileInputFormat.setInputPaths(conf1, new Path(args[0]));
FileOutputFormat.setOutputPath(conf1, new Path(args[1]));
JobClient.runJob(conf1);
JobConf conf2 = new JobConf(Final.class);
conf2.setJobName("Final");
conf2.setOutputKeyClass(Text.class);
conf2.setOutputValueClass(Text.class);
conf2.setMapperClass(Final.MapB.class);
//conf.setCombinerClass(Combine.class);
conf2.setReducerClass(Final.ReduceB.class);
conf2.setInputFormat(TextInputFormat.class);
conf2.setOutputFormat(TextOutputFormat.class);
FileInputFormat.setInputPaths(conf2, new Path(args[1]));
FileOutputFormat.setOutputPath(conf2, new Path(args[2]));
JobClient.runJob(conf2);
}
}
class Final
{
public static class MapB extends MapReduceBase implements Mapper<LongWritable, Text, Text, Text>
{
public void map(LongWritable key, Text value, OutputCollector<Text,Text> output, Reporter reporter) throws IOException
{
String[] r = value.toString().split("\r?\n");
String[] p1= new String[5];
for(int i=0;i<r.length;i++)
{
p1 = r[i].split("\t");
output.collect(new Text(p1[0]),new Text(p1[1]));
}
}
}
public static class ReduceB extends MapReduceBase implements Reducer<Text, Text, Text, Text>
{
@Override
public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text>output, Reporter reporter) throws IOException
{
int sum=0;
while(values.hasNext())
{
String s = (values.next()).toString();
int c=Integer.parseInt(s);
sum+=c;
}
float avf =(float)sum/3;
String count=Float.toString(avf);
output.collect(key,new Text(count));
}
}
}
程序在这样的数据集上运行:
ID1,1,2,3
ID1,1,3,2
ID3,2,2,3
每一行都有一个 ID,后跟 3 个逗号分隔的属性。我的问题是找到每个 ID 的每个属性值的频率(如果数据集被视为二维数组,则沿着列而不是跨行),然后总结每个 ID 的每个属性的频率并找到平均值。因此对于上述数据集:
ID1 : 2+2+2/3=2
ID2 : 2+1+1/3=1.33
ID3 : 1+2+2/3=1.67
上述代码适用于 200-500MB 等小型数据集。但对于 1GB 以上的数据集,我会收到如下错误:
map 100% reduce 50%
14/04/12 12:33:06 INFO mapred.JobClient: Task Id : attempt_201404121146_0002_r_000001_0, Status : FAILED
Error: Java heap space
attempt_201404121146_0002_r_000001_0: Exception in thread "LeaseRenewer:hdfs@NameNode:8020" java.lang.OutOfMemoryError: Java heap space
attempt_201404121146_0002_r_000001_0: at org.apache.hadoop.hdfs.LeaseRenewer.renew(LeaseRenewer.java:397)
attempt_201404121146_0002_r_000001_0: at org.apache.hadoop.hdfs.LeaseRenewer.run(LeaseRenewer.java:436)
attempt_201404121146_0002_r_000001_0: at org.apache.hadoop.hdfs.LeaseRenewer.access$700(LeaseRenewer.java:70)
attempt_201404121146_0002_r_000001_0: at org.apache.hadoop.hdfs.LeaseRenewer$1.run(LeaseRenewer.java:297)
attempt_201404121146_0002_r_000001_0: at java.lang.Thread.run(Thread.java:662)
attempt_201404121146_0002_r_000001_0: Exception in thread "Thread for syncLogs" java.lang.OutOfMemoryError: Java heap space
attempt_201404121146_0002_r_000001_0: at java.util.AbstractList.iterator(AbstractList.java:273)
attempt_201404121146_0002_r_000001_0: at org.apache.hadoop.mapred.TaskLog.syncLogs(TaskLog.java:363)
attempt_201404121146_0002_r_000001_0: at org.apache.hadoop.mapred.Child$3.run(Child.java:158)
14/04/12 12:33:10 INFO mapred.JobClient: map 100% reduce 33%
14/04/12 12:33:12 INFO mapred.JobClient: Task Id : attempt_201404121146_0002_r_000003_0, Status : FAILED
Error: Java heap space
attempt_201404121146_0002_r_000003_0: log4j:WARN No appenders could be found for logger (org.apache.hadoop.mapred.Task).
attempt_201404121146_0002_r_000003_0: log4j:WARN Please initialize the log4j system properly.
attempt_201404121146_0002_r_000003_0: log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
14/04/12 12:33:15 INFO mapred.JobClient: map 100% reduce 16%
14/04/12 12:33:16 INFO mapred.JobClient: map 100% reduce 18%
14/04/12 12:33:16 INFO mapred.JobClient: Task Id : attempt_201404121146_0002_r_000000_0, Status : FAILED
Error: Java heap space
attempt_201404121146_0002_r_000000_0: Exception in thread "LeaseRenewer:hdfs@NameNode:8020" java.lang.OutOfMemoryError: Java heap space
attempt_201404121146_0002_r_000000_0: at java.lang.StringCoding.set(StringCoding.java:53)
attempt_201404121146_0002_r_000000_0: at java.lang.StringCoding.decode(StringCoding.java:171)
attempt_201404121146_0002_r_000000_0: at java.lang.String.<init>(String.java:443)
attempt_201404121146_0002_r_000000_0: at java.util.jar.Attributes.read(Attributes.java:401)
attempt_201404121146_0002_r_000000_0: at java.util.jar.Manifest.read(Manifest.java:182)
attempt_201404121146_0002_r_000000_0: at java.util.jar.Manifest.<init>(Manifest.java:52)
attempt_201404121146_0002_r_000000_0: at java.util.jar.JarFile.getManifestFromReference(JarFile.java:167)
attempt_201404121146_0002_r_000000_0: at java.util.jar.JarFile.getManifest(JarFile.java:148)
attempt_201404121146_0002_r_000000_0: at sun.misc.URLClassPath$JarLoader$2.getManifest(URLClassPath.java:696)
attempt_201404121146_0002_r_000000_0: at java.net.URLClassLoader.defineClass(URLClassLoader.java:228)
attempt_201404121146_0002_r_000000_0: at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
attempt_201404121146_0002_r_000000_0: at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
attempt_201404121146_0002_r_000000_0: at java.security.AccessController.doPrivileged(Native Method)
attempt_201404121146_0002_r_000000_0: at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
attempt_201404121146_0002_r_000000_0: at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
attempt_201404121146_0002_r_000000_0: at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
attempt_201404121146_0002_r_000000_0: at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
attempt_201404121146_0002_r_000000_0: at org.apache.hadoop.hdfs.LeaseRenewer.renew(LeaseRenewer.java:400)
attempt_201404121146_0002_r_000000_0: at org.apache.hadoop.hdfs.LeaseRenewer.run(LeaseRenewer.java:436)
attempt_201404121146_0002_r_000000_0: at org.apache.hadoop.hdfs.LeaseRenewer.access$700(LeaseRenewer.java:70)
attempt_201404121146_0002_r_000000_0: at org.apache.hadoop.hdfs.LeaseRenewer$1.run(LeaseRenewer.java:297)
attempt_201404121146_0002_r_000000_0: at java.lang.Thread.run(Thread.java:662)
14/04/12 12:33:21 INFO mapred.JobClient: map 100% reduce 20%
我认为我的程序消耗了太多内存,需要优化。我什至试图通过将我的 java 堆空间增加到 1024MB 来解决这个问题,但我仍然遇到同样的错误。我使用的数据集是 1.4GB,它有 5cr 行和 9 个属性,不包括行 ID。由于我的问题是大数据,用小数据测试代码不是解决方案。请你建议我如何优化我的代码以解决内存问题。提前致谢。
【问题讨论】:
-
在您的第一份工作中,您将与特定键对应的所有值保留在列表中。由于您有 5cr 行并且每行有 9 个属性,因此与特定键对应的所有值的大小将对于Java中的普通List来说太大而无法保存在堆内存中。这就是
java.lang.OutOfMemoryError: Java heap space异常的原因。您必须避免将所有值对应于Java堆中的对象中的键。 -
在我的第一个 MapReduce 的减少达到 66% 后,我收到了这个错误。所以这一定是问题所在。
-
是的,您可以尝试更改该列表
-
@donut:根据我的reduce函数任务,我需要遍历值的迭代器两次——一次,对值进行计数,然后将迭代器文本值输出为键并计数为值。如何遍历迭代器两次?是否有任何替代方法可以实现类似的逻辑?
-
@MonamiSen 你能否给出一个更清晰的例子来说明你想要实现的目标。可能还有另一种方法。