三种方法实现Hadoop(MapReduce)全局排序(1)
我们可能会有些需求要求MapReduce的输出全局有序,这里说的有序是指Key全局有序。但是我们知道,MapReduce默认只是保证同一个分区内的Key是有序的,但是不保证全局有序。基于此,本文提供三种方法来对MapReduce的输出进行全局排序。
|文章目录|
|:
|1.生成测试数据
|2.使用一个Reduce进行排序
|3.自定义分区函数实现全局有序
1.生成测试数据
在介绍如何实现之前,我们先来生成一些测试数据,实现如下:
```
#!/bin/sh
for i in {1..100000};do
echo $RANDOM
done;
将上面的代码保存到 `iteblog.sh` 的文件里面,然后运行
$ sh iteblog.sh > data1
$ sh iteblog.sh > data2
$ hadoop fs -put data1 /user/iteblog/input
$ hadoop fs -put data2 /user/iteblog/input
`$RANDOM` 变量是Shell内置的,使用它能够生成五位内的随机正整数。上面我们一共运行了两次,这样我们就有两份随机数文件data1和data2;最后我们把生成的随机数文件上传到HDFS上。现在我们可以来写程序对这两个文件里面的数据进行排序了。
<h2 id='2'>使用一个Reduce进行排序</h2>
前面我们说了,MapReduce默认只是保证同一个分区内的Key是有序的,但是不保证全局有序。如果我们将所有的数据全部发送到一个Reduce,那么不就可以实现结果全局有序吗?这种方法实现很简单,如下:
package com.iteblog.mapreduce.sort;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
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;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.io.IOException;
public class TotalSortV1 extends Configured implements Tool {
static class SimpleMapper extends
Mapper<LongWritable, Text, IntWritable, IntWritable> {
@Override
protected void map(LongWritable key, Text value,
Context context) throws IOException, InterruptedException {
IntWritable intWritable = new IntWritable(Integer.parseInt(value.toString()));
context.write(intWritable, intWritable);
}
}
static class SimpleReducer extends
        Reducer<IntWritable, IntWritable, IntWritable, NullWritable> {
    @Override
    protected void reduce(IntWritable key, Iterable<IntWritable> values,
                          Context context) throws IOException, InterruptedException {
        for (IntWritable value : values)
            context.write(value, NullWritable.get());
    }
}
@Override
public int run(String[] args) throws Exception {
    if (args.length != 2) {
        System.err.println("<input> <output>");
        System.exit(127);
    }
    Job job = Job.getInstance(getConf());
    job.setJarByClass(TotalSortV1.class);
    FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
    job.setMapperClass(SimpleMapper.class);
    job.setReducerClass(SimpleReducer.class);
    job.setMapOutputKeyClass(IntWritable.class);
    job.setMapOutputValueClass(IntWritable.class);
    job.setOutputKeyClass(IntWritable.class);
    job.setOutputValueClass(NullWritable.class);
    job.setNumReduceTasks(1);
    job.setJobName("TotalSort");
    return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
    int exitCode = ToolRunner.run(new TotalSort(), args);
    System.exit(exitCode);
}
}
上面程序的实现很简单,我们直接使用` TextInputFormat `类来读取上面生成的随机数文件(`data1 `和` data2`)。因为文件里面的数据是正整数,所以我们在 `SimpleMapper` 类里面直接将value转换成int类型,然后赋值给`IntWritable`。等数据到 `SimpleReducer` 的时候,同一个`Reduce`里面的`Key`已经全部有序;因为我们设置了一个`Reduce作业`,这样的话,我们就实现了数据全局有序。运行如下:
[iteblog@www.iteblog.com /home/iteblog]$ hadoop jar total-sort-0.1.jar com.iteblog.mapreduce.sort.TotalSortV1 /user/iteblog/input /user/iteblog/output
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -ls /user/iteblog/output
Found 2 items
-rw-r--r--   3 iteblog supergroup          0 2017-05-09 11:41 /user/iteblog/output/_SUCCESS
-rw-r--r--   3 iteblog supergroup    1131757 2017-05-09 11:41 /user/iteblog/output/part-r-00000
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output/part-r-00000 | head -n 10
0
0
0
0
1
1
1
1
1
1
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output/part-r-00000 | tail -n 10
32766
32766
32766
32766
32767
32767
32767
32767
32767
32767
从上面的测试结果也可以看出,我们只生成了一个数据文件,而且这个文件里面的数据已经全局有序了。
<h2  id='3'>自定义分区函数实现全局有序</h2>
上面实现数据全局有序有个很大的局限性:所有的数据都发送到一个`Reduce`进行排序,这样不能充分利用集群的计算资源,而且在数据量很大的情况下,很有可能会出现OOM问题。我们分析一下,`MapReduce`默认的分区函数是`HashPartitioner`,其实现的原理是计算map输出key的` hashCode `,然后对Reduce个数求模,这样只要求模结果一样的Key都会发送到同一个`Reduce`。如果我们能够实现一个分区函数,使得
* 所有 Key < 10000 的数据都发送到Reduce 0;
* 所有 10000 < Key < 20000 的数据都发送到Reduce 1;
* 其余的Key都发送到Reduce 2;
这就实现了Reduce 0的数据一定全部小于Reduce 1,且Reduce 1的数据全部小于Reduce 2,再加上同一个Reduce里面的数据局部有序,这样就实现了数据的全局有序。实现如下:
package com.iteblog.mapreduce.sort;
import com.iteblog.mapreduce.secondSort.IntPair;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Partitioner;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.io.IOException;
public class TotalSortV2 extends Configured implements Tool {
static class SimpleMapper extends Mapper<LongWritable, Text, IntWritable, IntWritable> {
@Override
protected void map(LongWritable key, Text value,
Context context) throws IOException, InterruptedException {
IntWritable intWritable = new IntWritable(Integer.parseInt(value.toString()));
context.write(intWritable, intWritable);
}
}
static class SimpleReducer extends Reducer<IntWritable, IntWritable, IntWritable, NullWritable> {
    @Override
    protected void reduce(IntWritable key, Iterable<IntWritable> values,
                          Context context) throws IOException, InterruptedException {
        for (IntWritable value : values)
            context.write(value, NullWritable.get());
    }
}
public static class IteblogPartitioner extends Partitioner<IntWritable, IntWritable> {
    @Override
    public int getPartition(IntWritable key, IntWritable value, int numPartitions) {
        int keyInt = Integer.parseInt(key.toString());
        if (keyInt < 10000) {
            return 0;
        } else if (keyInt < 20000) {
            return 1;
        } else {
            return 2;
        }
    }
}
@Override
public int run(String[] args) throws Exception {
    if (args.length != 2) {
        System.err.println("<input> <output>");
        System.exit(127);
    }
    Job job = Job.getInstance(getConf());
    job.setJarByClass(TotalSortV2.class);
    FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
    job.setMapperClass(SimpleMapper.class);
    job.setReducerClass(SimpleReducer.class);
    job.setPartitionerClass(IteblogPartitioner.class);
    job.setMapOutputKeyClass(IntWritable.class);
    job.setMapOutputValueClass(IntWritable.class);
    job.setOutputKeyClass(IntWritable.class);
    job.setOutputValueClass(NullWritable.class);
    job.setNumReduceTasks(3);
    job.setJobName("dw_subject");
    return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
    int exitCode = ToolRunner.run(new TotalSortV2(), args);
    System.exit(exitCode);
}
}
第二版的排序实现除了自定义的 IteblogPartitioner,其余的和第一种实现一样。现在我们来运行一下:
[iteblog@www.iteblog.com /home/iteblog]$ hadoop jar total-sort-0.1.jar com.iteblog.mapreduce.sort.TotalSortV2 /user/iteblog/input /user/iteblog/output1
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -ls /user/iteblog/output1
Found 4 items
-rw-r--r--   3 iteblog supergroup          0 2017-05-09 13:53 /user/iteblog/output1/_SUCCESS
-rw-r--r--   3 iteblog supergroup     299845 2017-05-09 13:53 /user/iteblog/output1/part-r-00000
-rw-r--r--   3 iteblog supergroup     365190 2017-05-09 13:53 /user/iteblog/output1/part-r-00001
-rw-r--r--   3 iteblog supergroup     466722 2017-05-09 13:53 /user/iteblog/output1/part-r-00002
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output1/part-r-00000 | head -n 10
0
0
0
0
1
1
1
1
1
1
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output1/part-r-00000 | tail -n 10
9998
9998
9998
9999
9999
9999
9999
9999
9999
9999
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output1/part-r-00001 | head -n 10
10000
10000
10000
10000
10000
10000
10001
10001
10001
10001
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output1/part-r-00001 | tail -n 10
19997
19997
19998
19998
19998
19998
19999
19999
19999
19999
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output1/part-r-00002 | head -n 10
20000
20000
20000
20000
20000
20000
20001
20001
20001
20001
[iteblog@www.iteblog.com /home/iteblog]$ hadoop fs -cat /user/iteblog/output1/part-r-00002 | tail -n 10
32766
32766
32766
32766
32767
32767
32767
32767
32767
32767
我们已经看到了这个程序生成了三个文件(因为我们设置了Reduce个数为3),而且每个文件都是局部有序;所有小于10000的数据都在part-r-00000里面,所有小于20000的数据都在part-r-00001里面,所有大于20000的数据都在part-r-00002里面。part-r-00000、part-r-00001和part-r-00002三个文件实现了全局有序。
再这里我还是要分享一下我新建的大数据qun:**784557197**, 欢迎大家加入												
											三种方法实现Hadoop(MapReduce)全局排序(1)的更多相关文章
- 让程序只运行一个实例(Delphi篇)(三种方法,其中使用全局原子的方法比较有意思)
		
Windows 下一个典型的特征就是多任务,我们可以同时打开多个窗口进行操作,也可以同时运行程序的多个实例,比如可以打开许多个资源管理器进行文件的移动复制操作.但有时出于某种考虑(比如安全性),我们要 ...
 - YbSoftwareFactory 代码生成插件【二十五】:Razor视图中以全局方式调用后台方法输出页面代码的三种方法
		
上一篇介绍了 MVC中实现动态自定义路由 的实现,本篇将介绍Razor视图中以全局方式调用后台方法输出页面代码的三种方法. 框架最新的升级实现了一个页面部件功能,其实就是通过后台方法查询数据库内容,把 ...
 - 大数据mapreduce全局排序top-N之python实现
		
a.txt.b.txt文件如下: a.txt hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop ...
 - 本文将介绍“数据计算”环节中常用的三种分布式计算组件——Hadoop、Storm以及Spark。
		
本文将介绍“数据计算”环节中常用的三种分布式计算组件——Hadoop.Storm以及Spark. 当前的高性能PC机.中型机等机器在处理海量数据时,其计算能力.内存容量等指标都远远无法达到要求.在大数 ...
 - 服务器文档下载zip格式  SQL Server SQL分页查询  C#过滤html标签  EF 延时加载与死锁  在JS方法中返回多个值的三种方法(转载)  IEnumerable,ICollection,IList接口问题  不吹不擂,你想要的Python面试都在这里了【315+道题】   基于mvc三层架构和ajax技术实现最简单的文件上传  事件管理
		
服务器文档下载zip格式 刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...
 - JAVA之线程同步的三种方法
		
最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...
 - 【SQL】Oracle分页查询的三种方法
		
[SQL]Oracle分页查询的三种方法 采用伪列 rownum 查询前10条记录 ? 1 2 3 4 5 6 7 8 9 10 11 [sql] select * from t_user t whe ...
 - 【转】asp.net导出数据到Excel的三种方法
		
来源:http://www.cnblogs.com/lishengpeng1982/archive/2008/04/03/1135490.html 原文出处:http://blog.csdn.net/ ...
 - Logstash处理json格式日志文件的三种方法
		
假设日志文件中的每一行记录格式为json的,如: {"Method":"JSAPI.JSTicket","Message":"JS ...
 
随机推荐
- Java提高班(五)深入理解BIO、NIO、AIO
			
导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...
 - Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据
			
一.简介 在当下这个前后端分离的技术趋势下,前端工程师过度依赖后端工程师的接口和数据,给开发带来了两大问题: 问题一.后端接口查看难:要怎么调用?参数怎么传递?有几个参数?参数都代表什么含义? 问题二 ...
 - 杭电ACM2003--求绝对值
			
求绝对值 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
 - 【Unity】微软的一款依赖注入组件
			
前言 前面学习了autofac这个依赖注入组件,本来是打算写在一起的,因为这个组件没打算像autofac一样详细的写,只是写下以前自己鼓捣玩搭建框架然后使用的一个依赖注入组件,并且也是进行了封装使用. ...
 - Softmax函数模型介绍
			
Softmax在机器学习中有非常广泛的应用,但是刚刚接触机器学习的人可能对Softmax的特点以及好处并不理解,其实你了解了以后就会发现,Softmax计算简单,效果显著,非常好用. 我们先来直观看一 ...
 - react create-react-app 怎么添加sass
			
一.先上官方文档 https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/RE ...
 - 后端开发者的Vue学习之路(二)
			
目录 上篇内容回顾: 数据绑定 表单输入框绑定 单行文本输入框 多行文本输入框 复选框checkbox 单选框radio 选择框select 数据绑定的修饰符 .lazy .number .trim ...
 - SAP MM MI01事务代码里的批次确定
			
SAP MM MI01事务代码里的批次确定 1 – 批次管理启用之后果 一个物料如果启用了批次管理,那么库存管理以及盘点等诸多事务里都需要在批次的层次上进行. 货物移动的时候,需要在界面上指定相关货物 ...
 - android常犯错误记录(二)
			
检查 minSdkVersion什么的是不是和你依赖的包一样,它上面也有个小提示,显示本地的11,依赖的为15,那就改成15好了,重新build好了 ClassNotFoundException异常 ...
 - ASP.NET没有魔法——ASP.NET MVC IoC代码篇
			
上一篇文章主要以文字的形式介绍了IoC及其在ASP.NET MVC中的使用,本章将从以下几点介绍如何使用代码在ASP.NET MVC中实现依赖注入: ● AutoFac及安装 ● 容器的创建 ● 创建 ...