排序

排序是MapReduce的核心技术。

1.准备

示例:按照气温字段对天气数据集排序。由于气温字段是有符号的整数,所以不能将该字段视为Text对象并以字典顺序排序。反之,用顺序文件存储数据,其IntWritable键代表气温(并且正确排序),其Text值就是数据行。
MapReduce作业只包含map任务,它过滤输入数据并移除空数据行的记录。各个map创建并输出一个块压缩的顺序文件。
代码如下

package com.zhen.mapreduce.sort.preprocessor;

import java.io.IOException;

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.SequenceFile.CompressionType;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; /**
* @author FengZhen
* @date 2018年9月9日
* 过滤掉无用数据并使用顺序文件存储数据
*/
public class SortDataPreprocessor extends Configured implements Tool{ static class CleanerMapper extends Mapper<LongWritable, Text, IntWritable, Text>{
private RecordParser recordParser = new RecordParser();
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, IntWritable, Text>.Context context)
throws IOException, InterruptedException {
recordParser.parse(value.toString());
if (recordParser.isValidTemperature()) {
context.write(new IntWritable(recordParser.getTemperature()), new Text(recordParser.getCity()));
}
}
} public int run(String[] args) throws Exception { Job job = Job.getInstance(getConf());
job.setJobName("SortDataPreprocessor");
job.setJarByClass(SortDataPreprocessor.class); job.setMapperClass(CleanerMapper.class);
job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(Text.class);
job.setNumReduceTasks(0);
job.setOutputFormatClass(SequenceFileOutputFormat.class); FileInputFormat.setInputPaths(job, new Path(args[0])); SequenceFileOutputFormat.setOutputPath(job, new Path(args[1]));
//是否被压缩都会被输出
SequenceFileOutputFormat.setCompressOutput(job, true);
SequenceFileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
SequenceFileOutputFormat.setOutputCompressionType(job, CompressionType.BLOCK); return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) throws Exception {
String[] params = new String[]{"hdfs://fz/user/hdfs/MapReduce/data/sort/preprocessor/input","hdfs://fz/user/hdfs/MapReduce/data/sort/preprocessor/output"};
int exitCode = ToolRunner.run(new SortDataPreprocessor(), params);
System.exit(exitCode);
} }

  

package com.zhen.mapreduce.sort.preprocessor;

import java.io.Serializable;

/**
* @author FengZhen
* @date 2018年9月9日
* 解析MapReduce中map的数据
*/
public class RecordParser implements Serializable{ private static final long serialVersionUID = 1L; /**
* 城市
*/
private String city;
/**
* 气温
*/
private Integer temperature; /**
* 解析
* @param value
*/
public void parse(String value) {
String[] values = value.split(",");
if (values.length >= 2) {
city = values[0];
temperature = Integer.valueOf(values[1]);
}
} /**
* 校验是否合格
* @return
*/
public boolean isValidTemperature() {
return null != temperature;
} public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getTemperature() {
return temperature;
}
public void setTemperature(Integer temperature) {
this.temperature = temperature;
} }

  

打jar包上传至服务器执行

scp /Users/FengZhen/Desktop/Hadoop/file/Sort.jar root@192.168.1.124:/usr/local/test/mr
hadoop jar Sort.jar com.zhen.mapreduce.sort.SortDataPreprocessor

2.部分排序

当有多个reduce任务时,产生多个已排序的输出文件。但是如何将这些小文件合并成一个有序的文件却并非易事。

3.全排序

如何使用Hadoop产生一个全局排序的文件?最简单的方法是使用一个分区(a single partition)。但该方法在处理大型文件时效率极低,因为一台机器必须处理所有输出文件,从而完全丧失了MapReduce所提供的并行架构的优势。
事实上仍有替代方案:首先,创建一系列排好序的文件;其次,串联这些文件;最后,生成一个全局排序的文件。主要的思路是使用一个partitioner来描述输出的全局排序。
示例:以气温排序为例
给定一个partitioner,四个分区,第一个分区的温度范围在0-10,第二个在11-20,第三个在21-30,第四个在31-40.
这样可以保证第i个分区的键小于第i+1个分区的键,保证了完全有序,但是会出现数据分布不均的情况。
获得气温分布信息意味着可以建立一系列分布非常均匀的分区。但由于该操作需要遍历整个数据集,因此并不实用。通过对键空间进行采样,就可较为均匀地划分数据集。采样的核心思想是只查看一小部分键,获得键的近似分布,并由此构建分区。Hadoop已经内置了若干采样器。
InputSampler类实现了Sampler接口,该接口的唯一成员方法(getSampler)有两个输入参数(一个InputFormat对象和一个Job对象),返回一系列样本键。
代码如下

package com.zhen.mapreduce.sort.totalPartitioner;

import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile.CompressionType;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.InputSampler;
import org.apache.hadoop.mapreduce.lib.partition.TotalOrderPartitioner;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; /**
* @author FengZhen
* @date 2018年9月9日
* 根据分区全排序
*/
public class SortByTemperatureUsingTotalOrderPartitioner extends Configured implements Tool{ public int run(String[] args) throws Exception {
Job job = Job.getInstance(getConf());
job.setJobName("SortByTemperatureUsingTotalOrderPartitioner");
job.setJarByClass(SortByTemperatureUsingTotalOrderPartitioner.class); job.setInputFormatClass(SequenceFileInputFormat.class);
job.setOutputFormatClass(SequenceFileOutputFormat.class); job.setOutputKeyClass(IntWritable.class);
job.setOutputFormatClass(SequenceFileOutputFormat.class); SequenceFileInputFormat.setInputPaths(job, new Path(args[0]));
SequenceFileOutputFormat.setOutputPath(job, new Path(args[1]));
//是否被压缩都会被输出
SequenceFileOutputFormat.setCompressOutput(job, true);
SequenceFileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
SequenceFileOutputFormat.setOutputCompressionType(job, CompressionType.BLOCK); job.setPartitionerClass(TotalOrderPartitioner.class);
/**
* 采样率设为 0.1
* 最大样本数 10000
* 最大分区数 10
* 这也是InputSampler作为应用程序运行时的默认设置
* 只要任意一个限制条件满足,即停止采样。
*/
InputSampler.Sampler<IntWritable, Text> sampler = new InputSampler.RandomSampler(0.1, 10000, 10);
InputSampler.writePartitionFile(job, sampler); //为了和集群上运行的其他任务共享分区文件,InputSampler需要将其所写的分区文件加到分布式缓存中。
Configuration conf = job.getConfiguration();
String partitionFile = TotalOrderPartitioner.getPartitionFile(conf);
URI partitionUri = new URI(partitionFile + "#" + TotalOrderPartitioner.DEFAULT_PATH);
job.addCacheFile(partitionUri);
job.createSymlink(); return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) throws Exception {
String[] params = new String[]{"hdfs://fz/user/hdfs/MapReduce/data/sort/preprocessor/output","hdfs://fz/user/hdfs/MapReduce/data/sort/SortByTemperatureUsingTotalOrderPartitioner/output"};
int exitCode = ToolRunner.run(new SortByTemperatureUsingTotalOrderPartitioner(), params);
System.exit(exitCode);
} }

  

4.辅助排序

MapReduce框架在记录到达reducer之前按键对记录排序,但键所对应的值并没有被排序。
示例:键升序,键相同的值升序
代码如下

package com.zhen.mapreduce.sort.secondarySort;

import java.io.IOException;

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.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
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; /**
* @author FengZhen
* @date 2018年9月9日
* 对键排序后的值排序
*/
public class MaxTemperatureUsingSecondarySort extends Configured implements Tool{ static class MaxTemperatureMapper extends Mapper<LongWritable, Text, IntPair, NullWritable>{ private RecordParser recordParser = new RecordParser(); @Override
protected void map(LongWritable key, Text value,
Mapper<LongWritable, Text, IntPair, NullWritable>.Context context)
throws IOException, InterruptedException {
recordParser.parse(value.toString());
if (recordParser.isValidTemperature()) {
context.write(new IntPair(recordParser.getYear(), recordParser.getTemperature()), NullWritable.get());
}
}
} static class MaxTemperatureReducer extends Reducer<IntPair, NullWritable, IntPair, NullWritable>{
@Override
protected void reduce(IntPair key, Iterable<NullWritable> values,
Reducer<IntPair, NullWritable, IntPair, NullWritable>.Context context)
throws IOException, InterruptedException {
context.write(key, NullWritable.get());
}
} /**
* 创建一个自定义的partitioner以按照组合键的守字段(年份)进行分区
* @author FengZhen
*
*/
public static class FirstPartitioner extends Partitioner<IntWritable, IntWritable>{
@Override
public int getPartition(IntWritable key, IntWritable value, int numPartitions) {
return Math.abs(key.get() * 127) % numPartitions;
}
} /**
* 按照年份(升序)和气温(降序)排列键
* @author FengZhen
*
*/
public static class KeyComparator extends WritableComparator{
public KeyComparator() {
super(IntPair.class, true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
IntPair ip1 = (IntPair) a;
IntPair ip2 = (IntPair) b;
int cmp = IntPair.compare(ip1.getFirstKey(), ip2.getFirstKey());
if (cmp != 0) {
return cmp;
}
return -IntPair.compare(ip1.getSecondKey(), ip2.getSecondKey());
}
} /**
* 按年份对键进行分组
* @author FengZhen
*
*/
public static class GroupComparator extends WritableComparator {
protected GroupComparator() {
super(IntPair.class, true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
IntPair ip1 = (IntPair) a;
IntPair ip2 = (IntPair) b;
return IntPair.compare(ip1.getFirstKey(), ip2.getFirstKey());
}
} public int run(String[] args) throws Exception {
Job job = Job.getInstance(getConf());
job.setJobName("MaxTemperatureUsingSecondarySort");
job.setJarByClass(MaxTemperatureUsingSecondarySort.class); job.setMapperClass(MaxTemperatureMapper.class);
job.setReducerClass(MaxTemperatureReducer.class); job.setPartitionerClass(FirstPartitioner.class);
job.setSortComparatorClass(KeyComparator.class);
job.setGroupingComparatorClass(GroupComparator.class); job.setOutputKeyClass(IntPair.class);
job.setOutputValueClass(NullWritable.class); FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); return job.waitForCompletion(true) ? 0 : 1;
} public static void main(String[] args) throws Exception {
String[] params = new String[] {"hdfs://fz/user/hdfs/MapReduce/data/sort/MaxTemperatureUsingSecondarySort/input", "hdfs://fz/user/hdfs/MapReduce/data/sort/MaxTemperatureUsingSecondarySort/output"};
int exitCode = ToolRunner.run(new MaxTemperatureUsingSecondarySort(), params);
System.exit(exitCode);
}
}

  

IntPair:自定义组合键

package com.zhen.mapreduce.sort.secondarySort;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException; import org.apache.hadoop.io.WritableComparable; /**
* 自定义组合键
* map的键排序
* */
public class IntPair implements WritableComparable{ //使用java基本数据类型
private int firstKey;
private int secondKey;
public IntPair() {
}
public IntPair(int firstKey, int secondKey) {
this.firstKey = firstKey;
this.secondKey = secondKey;
}
//必须有默认的构造函数
public int getFirstKey() {
return firstKey;
}
public void setFirstKey(int firstKey) {
this.firstKey = firstKey;
}
public int getSecondKey() {
return secondKey;
}
public void setSecondKey(int secondKey) {
this.secondKey = secondKey;
} public void readFields(DataInput in) throws IOException {
firstKey = in.readInt();
secondKey = in.readInt();
} public void write(DataOutput out) throws IOException {
out.writeInt(firstKey);
out.writeInt(secondKey);
} /**
* map的键的比较就是根据这个方法来进行
* */
public int compareTo(Object o) {
IntPair tInt = (IntPair)o;
//利用这个来控制升序或降序
//this在前为升序
//this在后为降序
return this.getFirstKey() >= (tInt.getFirstKey()) ? -1 : 1;
} /**
* 比较两个int值大小
* 降序
* @param a
* @param b
* @return
*/
public static int compare(int a, int b) {
return a >= b ? -1 : 1;
}
@Override
public String toString() {
return "IntPair [firstKey=" + firstKey + ", secondKey=" + secondKey + "]";
} }

  

RecordParser:解析每条记录

package com.zhen.mapreduce.sort.secondarySort;

import java.io.Serializable;

/**
* @author FengZhen
* @date 2018年9月9日
* 解析MapReduce中map的数据
*/
public class RecordParser implements Serializable{ private static final long serialVersionUID = 1L; /**
* 年份
*/
private Integer year;
/**
* 气温
*/
private Integer temperature; /**
* 解析
* @param value
*/
public void parse(String value) {
String[] values = value.split(",");
if (values.length >= 2) {
year = Integer.valueOf(values[0]);
temperature = Integer.valueOf(values[1]);
}
} /**
* 校验是否合格
* @return
*/
public boolean isValidTemperature() {
return null != temperature;
} public Integer getYear() {
return year;
} public void setYear(Integer year) {
this.year = year;
} public int getTemperature() {
return temperature;
}
public void setTemperature(Integer temperature) {
this.temperature = temperature;
}
}

  

原始数据如下

1990,14
1980,12
1990,19
1960,11
1960,18
1980,17
1970,24
1970,23
1940,22
1940,35
1930,44
1920,43

输出数据如下:输出数据格式可重写IntPair的toString方法

IntPair [firstKey=1990, secondKey=19]
IntPair [firstKey=1990, secondKey=14]
IntPair [firstKey=1980, secondKey=17]
IntPair [firstKey=1980, secondKey=12]
IntPair [firstKey=1970, secondKey=23]
IntPair [firstKey=1970, secondKey=24]
IntPair [firstKey=1960, secondKey=18]
IntPair [firstKey=1960, secondKey=11]
IntPair [firstKey=1940, secondKey=35]
IntPair [firstKey=1940, secondKey=22]
IntPair [firstKey=1930, secondKey=44]
IntPair [firstKey=1920, secondKey=43]

  

MapReduce-排序(全部排序、辅助排序)的更多相关文章

  1. 辅助排序和Mapreduce整体流程

    一.辅助排序 需求:先有一个订单数据文件,包含了订单id.商品id.商品价格,要求将订单id正序,商品价格倒序,且生成结果文件个数为订单id的数量,每个结果文件中只要一条该订单最贵商品的数据. 思路: ...

  2. hadoop MapReduce辅助排序解析

    1.数据样本,w1.csv到w5.csv,每个文件数据样本2000条,第一列是年份从1990到2000随机,第二列数据从1-100随机,本例辅助排序目标是找出每年最大值,实际上结果每年最大就是100, ...

  3. Mapreduce的排序(全局排序、分区加排序、Combiner优化)

    一.MR排序的分类 1.部分排序:MR会根据自己输出记录的KV对数据进行排序,保证输出到每一个文件内存都是经过排序的: 2.全局排序: 3.辅助排序:再第一次排序后经过分区再排序一次: 4.二次排序: ...

  4. Hadoop案例(八)辅助排序和二次排序案例(GroupingComparator)

    辅助排序和二次排序案例(GroupingComparator) 1.需求 有如下订单数据 订单id 商品id 成交金额 0000001 Pdt_01 222.8 0000001 Pdt_05 25.8 ...

  5. mapreduce任务中Shuffle和排序的过程

    mapreduce任务中Shuffle和排序的过程 流程分析: Map端: 1.每个输入分片会让一个map任务来处理,默认情况下,以HDFS的一个块的大小(默认为64M)为一个分片,当然我们也可以设置 ...

  6. 【原创】MapReduce编程系列之二元排序

    普通排序实现 普通排序的实现利用了按姓名的排序,调用了默认的对key的HashPartition函数来实现数据的分组.partition操作之后写入磁盘时会对数据进行排序操作(对一个分区内的数据作排序 ...

  7. MapReduce:将下面的两排数字先按第一排排序,然后再按第二排排序,要求顺序排序

    MapReduce:将下面的两排数字先按第一排排序,然后再按第二排排序,要求顺序排序 文件如下: 这个案例主要考察我们对排序的理解,我们可以这样做: 代码如下(由于水平有限,不保证完全正确,如果发现错 ...

  8. 归并排序 & 计数排序 & 基数排序 & 冒泡排序 & 选择排序 ----> 内部排序性能比较

    2.3 归并排序 接口定义: int merge(void* data, int esize, int lpos, int dpos, int rpos, int (*compare)(const v ...

  9. Java常见排序算法之Shell排序

    在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...

随机推荐

  1. 深入理解line

    什么是行间距? 古时候我们使用印刷机来出来文字.印刷出来的每个字,都位于独立的一个块中. 行间距,即传说中控制两行文字垂直距离的东东.在CSS中,line-height被用来控制行与行之间垂直距离. ...

  2. [转]Linux Socket编程 Socket抓取网页源码

    “一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览 ...

  3. SharePoint服务器端对象模型 之 使用CAML进行数据查询(Part 3)

    (四)使用SPSiteDataQuery进行多列表查询 1.概述 前面介绍的列表查询有很多优势,但是它的一个缺点就是一次只能在一个列表中进行查询,在SharePoint中,提供了一个跨网站.跨列表查询 ...

  4. 集合遍历的时候删除List

    在Java中有时候我们会需要对List里面的符合某种业务的数据进行删除,但是如果不了解里面的机制就容易掉入“陷阱”导致遗漏或者程序异常.本文以代码例子的方式进行说明该问题. 1.采用索引下标遍历的方式 ...

  5. 路径 php中'.'和'..'还有'./'和'../'

    ./当前目录(就是当前执行文件所在目录) ../上级目录 / 这个才是根目文件名/ 同级目录 例子如图 1.cart下的index.php 1)要引用Public->css->index. ...

  6. 我的Android进阶之旅------>Android百度地图定位SDK功能学习

    因为项目需求,需要使用百度地图的定位功能,因此去百度地图开发平台下载了百度地图的Android定位SDK最新版本的开发包和示例代码学习. Android 定位SDK地址:http://develope ...

  7. git原理:pack打包

    git向磁盘中存储对象使用“松散(loose)”对象格式.比如文件a.txt第一个版本大小是10k,第二个版本向其中添加了一行代码,假如此时文件为10.1k,那么第二个版本会重新产生一个1.1k的文件 ...

  8. 字符串之strcmp

    功能:比较两个字符串的ascII码大小 输入:两个字符串 返回值:相等为0,大于为大于零,小于为小于零 #include <iostream> #include <assert.h& ...

  9. MFC中修改程序图标

    在使用MFC时,我们经常需要修改我们得到的exe文件的图标.如:写一个随机画圆的小程序,我们就希望该程序的图标是个圆或者是和圆有关的图标.所以,在这里我就记录一下我修改图标的步骤. 顺便提一下,我使用 ...

  10. 剑指offer 面试5题

    面试5题: 题目:请实现一个函数,将一个字符串中的空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 方法一: # -*- co ...