MapReduce理解
数据量过大,使用单机去计算,会面临一个难题,那就是pc机去计算一个T级,甚至P级别的数据时,该怎么处理?以一个单词统计来说?有2个T的文件,统计单词个数;如果传统写程序,pc机来跑有两个问题:①能否把在处理时把数据全部加载进入内存?②执行的效率?从传统的单pc机来跑程序可以看出,就是程序一条一条的读取记录,把每一条记录或者每一个单词加载进入内存,整体来看是一条一条的数据向程序靠拢,问题就是数据能不能全部进内存运行,存储的过程会不会溢出,大批量的数据移动,效率太低下;MapReduce正是解决上述两个问题,本身hdfs中的文件都是分布式存储,存在不同的datanode中,而所有的datanode的信息,会汇报给namenode。MapReduce就会根据namenode的储存的块信息,寻找要计算的文件所在的位置(Map过程)<K,V>,然后分别在每个datanode上来跑程序,分别计算<K,V>,然后再把计算结果汇总起来(Reduce过程)。从整体上看,是程序流向数据,速度相对来说会快的多。
自己写的wordCount程序:备注已详细加上了
Map类
/**
* 首先,自己手写的map方法第一步要继承自Mapper方法;传递要映射的key-value类型
* 默认情况下,key是文件的起始偏移量,value文件内容
* 对文件进行切分,存放成键值对的形式;使用的是StringUtils类;
*
* Context用来记录和储存结果
* 输入和输出应该对应的类型 <56900 hello,boy><hello,boy 1>
* MapReduce每次读一行,就会调用一次map()方法
*
* */
public class MyMaper extends Mapper<LongWritable, Text, Text, LongWritable> {
@Override
public void map(LongWritable key,Text value,Context context) throws IOException, InterruptedException {
String val = value.toString();
//转化成字符串,容易遍历;切分是假设目前文件是以空格为界限,形如:hello word
String[]words = StringUtils.split(val," ");
for (String word : words) {
context.write(new Text(word), new LongWritable(1));
}
}
}
reduce类
/**
* 注意,经过map后,统计结果会传到reduce方法中,此时是这种<good,{1,1,1,1,1,1,1,1}>,reduce的作用就是把这些数字计算出来
* 首先继承Reduce,统计和
* */
public class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
@Override
public void reduce(Text key,Iterable<LongWritable> values,Context context) throws IOException, InterruptedException {
long count = 0;
for (LongWritable item : values) {
count += item.get();
}
context.write(key, new LongWritable(count));
}
}
运行主类
/**
* 这里实现计数
* 1.首先新建连接对象,获取到连接的配置文件;然后设定job运行提交所在类
* 2.设定job的map类和reduce类
* 3.指定map和reduce的输出数据类型
* 4.指定要count的文件的输入路径和输出文件的保存路径
* 5.提交job;
*
* */
public class WordCount {
@Test
public void run() throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
Job countjob = Job.getInstance(conf);
countjob.setMapperClass(MyMaper.class);
countjob.setReducerClass(MyReducer.class);
countjob.setMapOutputKeyClass(Text.class);
countjob.setMapOutputValueClass(LongWritable.class);
countjob.setOutputKeyClass(Text.class);
countjob.setOutputValueClass(LongWritable.class);
FileInputFormat.setInputPaths(countjob, new Path("hdfs://SparkMaster:9000/input/hello.txt"));
FileOutputFormat.setOutputPath(countjob, new Path("hdfs://SparkMaster:9000/output/data/src"));
System.exit(countjob.waitForCompletion(true)?0:1);
}
}