一.Hadoop
转自:http://www.datalab.sinaapp.com/?p=92
Hadoop是一个实现了MapReduce计算模型的开源分布式并行编程框架。程序员可以借助Hadoop编写程序,将所编写的程序运行于计算机机群上,从而实现对海量数据的处理。此外,Hadoop还提供一个分布式文件系统(HDFS)及分布式数据库(HBase)用来将数据存储或部署到各个计算节点上。所以,可以大致认为:Hadoop=HDFS+HBase+MapReduce.
Hadoop任务处理的整个流程如下:
二:MapReduce
(input)<k1, v1> => map => <k2, v2> => combine => <k2, v2[List]> => reduce => <k3, v3[List]>(output)
三、MapReduce中的Shuffle
Shuffle是指从Map产生输出开始,包括系统执行排序以及传送Map输出到Reducer作为输入的过程。
四、Hadoop实例:CSDN十大密码
(1)输入文件的格式每行为一个账户(用户名#密码#邮箱),例如net123sky # e12345 # song123mail@21cn.com
(2)先通过map/reduce统计每个密码的出现个数,密码为key,再实现对key和value的交换,交换后出现次数为key,通过setSortComparatorClass指定排序函数,让hadoop即会按key降序排序。另外,由于本次试验是在伪分布下进行,只使用了一个Reduce,排序阶段并为指定分区函数。
实际中,利用Hadoop分而治之的计算模型进行全局排序,可以参照快排的思想。快速排序基本步骤就是需要现在所有数据中选取一个作为支点。然后将大于这个支点的放在一边,小于这个支点的放在另一边。然后对两边排序。
设想如果我们有N个支点(这里可以称为标尺),就可以把所有的数据分成N+1个part,将这N+1个part丢给N+1个reduce,每个part由Hadoop自动排序,最后输出N+1个内部有序的文件,再把这N+1个文件首尾相连合并成一个文件,形成全局有序的数据。
由此我们可以归纳出这样一个用hadoop对大量数据排序的步骤:
- 对待排序数据进行抽样;
- 对抽样数据进行排序,产生标尺;
- Map对输入的每条数据计算其处于哪两个标尺之间;将数据发给对应区间ID的reduce
- Reduce将获得数据直接输出。
这里只是实现了伪分布下单Reduce的全局排序,两轮MapReduce的代码见GitHub,运行结果如下:
其他参考资料:
百度搜索研发部官方博客《使用hadoop进行大规模数据的全局排序》
代码地址:https://github.com/intergret/snippet/blob/master/csdn.java
准备数据代码也来跑一遍吧:
package com.myhadoop.csdn600;
import java.io.IOException;
import java.util.Random;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class Csdn{
public static class CountMapper extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text password = new Text();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String eachline=value.toString();
String [] eachterm=eachline.split("#");
password.set(eachterm[1]);
context.write(password, one);
}
}
public static class CountReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable total = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
total.set(sum);
context.write(key,total);
}
}
public static class SortMapper extends Mapper<Object, Text, IntWritable,Text>{
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
IntWritable times = new IntWritable(1);
Text password = new Text();
String eachline=value.toString();
String[] eachterm =eachline.split(" ");
if(eachterm.length==2){
password.set(eachterm[0]);
times.set(Integer.parseInt(eachterm[1]));
context.write(times,password);
}else{
password.set("errorpassword");
context.write(times,password);
}
}
}
public static class SortReducer extends Reducer<IntWritable,Text,IntWritable,Text> {
private Text password = new Text();
public void reduce(IntWritable key,Iterable<Text> values, Context context) throws IOException, InterruptedException {
//不同的密码可能出现相同的次数
for (Text val : values) {
password.set(val);
context.write(key,password);
}
}
}
private static class IntDecreasingComparator extends IntWritable.Comparator {
public int compare(WritableComparable a, WritableComparable b) {
return -super.compare(a, b);
}
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
return -super.compare(b1, s1, l1, b2, s2, l2);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, "csdn");
job.setJarByClass(Csdn.class);
job.setMapperClass(CountMapper.class);
job.setCombinerClass(CountReducer.class);
job.setReducerClass(CountReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//定义一个临时目录,先将词频统计任务的输出结果写到临时目录中, 下一个排序任务以临时目录为输入目录。
FileInputFormat.addInputPath(job, new Path("input/www.csdn.net.sql"));
Path tempDir = new Path("csdn-temp-" + Integer.toString(new Random().nextInt(Integer.MAX_VALUE)));
FileOutputFormat.setOutputPath(job, tempDir);
if(job.waitForCompletion(true))
{
Job sortJob = new Job(conf, "csdnsort");
sortJob.setJarByClass(Csdn.class);
FileInputFormat.addInputPath(sortJob, tempDir);
sortJob.setMapperClass(SortMapper.class);
FileOutputFormat.setOutputPath(sortJob, new Path("output/csdnout"));
sortJob.setOutputKeyClass(IntWritable.class);
sortJob.setOutputValueClass(Text.class);
sortJob.setSortComparatorClass(IntDecreasingComparator.class);
FileSystem.get(conf).deleteOnExit(tempDir);
System.exit(sortJob.waitForCompletion(true) ? 0 : 1);
}
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
贴个图: