排序在很多业务场景都要用到,今天本文介绍如何借助于自定义Partition类实现hadoop部分排序。本文还是使用java和python实现排序代码。
1、部分排序。
部分排序就是在每个文件中都是有序的,和其他文件没有关系,其实很多业务场景就需要到部分排序,而不需要全局排序。例如,有个水果电商网站,要对每个月的水果的销量进行排序,我们可以把reduce进程之后的文件分成12份,对应1到12月份。每个文件按照水果的销量从高到底排序,1月份的排序和其他月份的排序没有任何关系。
原始数据如下,有三个字段,第一个字段是水果名称,第二个字段是销售月份,第三个字段是销售量,
Apple 201701 20
Pear 201701 30
Banana 201701 40
Orange 201701 90
Apple 201702 50
Pear 201702 60
Banana 201702 20
Orange 201702 10
Apple 201703 230
Pear 201703 302
Banana 201703 140
Orange 201703 290
Apple 201704 30
Pear 201704 102
Banana 201704 240
Orange 201704 190
经过部分排序后会生成12个文件,内容如下,销量按照从高到低排序
Pear 302
Orange 290
Apple 230
Banana 140
实现思路:
1、自定义Partition类,因为一年有12个月 ,因此需要12个分区,同时在MapReduce入口类中要指定Partition类,以及partition的数量。
2、在map函数中将年月作为key值,value变为“Apple_20”的格式。
3、在reduce函数中比较每种水果的销量,按照从高到低排序。
 
Java代码如下,Map类:
 public class PartSortMap extends Mapper<LongWritable,Text,Text,Text> {

     public void map(LongWritable key,Text value,Context context)throws IOException,InterruptedException{
String line = value.toString();//读取一行数据,数据格式为“Apple 201701 30”
String str[] = line.split(" ");//
//年月当做key值,因为要根据key值设置分区,而Apple+“_”+销量当做value
context.write(new Text(str[1]),new Text(str[0] + "_" + str[2]));
}
}
自定义Partition类:

 public class PartParttition extends Partitioner<Text, Text> {
public int getPartition(Text arg0, Text arg1, int arg2) {
String key = arg0.toString();
int month = Integer.parseInt(key.substring(4, key.length()));
if (month == 1) {
return 1 % arg2;
} else if (month == 2) {
return 2 % arg2;
} else if (month == 3) {
return 3 % arg2;
}else if (month == 4) {
return 4 % arg2;
}else if (month == 5) {
return 5 % arg2;
}else if (month == 6) {
return 6 % arg2;
}else if (month == 7) {
return 7 % arg2;
}else if (month == 8) {
return 8 % arg2;
}else if (month == 9) {
return 9 % arg2;
}else if (month == 10) {
return 10 % arg2;
}else if (month == 11) {
return 11 % arg2;
}else if (month == 12) {
return 12 % arg2;
}
return 0;
}
}
Reduce类:

 public class PartSortReduce extends Reducer<Text,Text,Text,Text> {
class FruitSales implements Comparable<FruitSales>{
private String name;//水果名字
private double sales;//水果销量
public void setName(String name){
this.name = name;
} public String getName(){
return this.name;
}
public void setSales(double sales){
this.sales = sales;
} public double getSales() {
return this.sales;
} @Override
public int compareTo(FruitSales o) {
if(this.getSales() > o.getSales()){
return -1;
}else if(this.getSales() == o.getSales()){
return 0;
}else {
return 1;
}
}
} public void reduce(Text key, Iterable<Text> values,Context context)throws IOException,InterruptedException{
List<FruitSales> fruitList = new ArrayList<FruitSales>(); for(Text value: values) {
String[] str = value.toString().split("_");
FruitSales f = new FruitSales();
f.setName(str[0]);
f.setSales(Double.parseDouble(str[1]));
fruitList.add(f);
}
Collections.sort(fruitList); for(FruitSales f : fruitList){
context.write(new Text(f.getName()),new Text(String.valueOf(f.getSales())));
}
}
}
入口类:

 public class PartSortMain {
public static void main(String[] args)throws Exception{
Configuration conf = new Configuration();
//获取运行时输入的参数,一般是通过shell脚本文件传进来。
String [] otherArgs = new GenericOptionsParser(conf,args).getRemainingArgs();
if(otherArgs.length < 2){
System.err.println("必须输入读取文件路径和输出路径");
System.exit(2);
}
Job job = new Job();
job.setJarByClass(PartSortMain.class);
job.setJobName("PartSort app"); //设置读取文件的路径,都是从HDFS中读取。读取文件路径从脚本文件中传进来
FileInputFormat.addInputPath(job,new Path(args[0])); //设置mapreduce程序的输出路径,MapReduce的结果都是输入到文件中
FileOutputFormat.setOutputPath(job,new Path(args[1])); job.setPartitionerClass(PartParttition.class);//设置自定义partition类
job.setNumReduceTasks(12);//设置为partiton数量
//设置实现了map函数的类
job.setMapperClass(PartSortMap.class); //设置实现了reduce函数的类
job.setReducerClass(PartSortReduce.class); //设置reduce函数的key值
job.setOutputKeyClass(Text.class);
//设置reduce函数的value值
job.setOutputValueClass(Text.class); System.exit(job.waitForCompletion(true) ? 0 :1);
}
}
运行后会在hdfs中生成12个文件,如下图所示:
 
查看其中的一个文件会看到如下的内容:

 
可以看到是按照销量从高到低排序。

 
使用Python实现部分排序。
Python使用streaming的方式实现MapReduce,和Java方式不一样,不能自定义Partition,但是可以在脚本文件中指定哪个字段用作partition,哪个字段用于排序。
下图显示数据经过部分排序之后,数据变化的过程。即原始数据,经过map函数,然后到reduce函数,最终在每个文件中按照销量从高到底排序的过程:

 
上图中的第一步是在map函数中将原始数据的第二列的“年月”转换成“月”,当做partition,将销量当做key,水果名当做value。第二步是经过MapReduce的排序之后到达Reduce函数之间的结果。第三步是在reduce函数中将map输入的数据中将key当做reduce的value,将value当做reduce的key。
代码如下:

map_sort.py

 #!/usr/bin/python
import sys
base_numer = 99999
for line in sys.stdin:
ss = line.strip().split(' ')
fruit = ss[0]
yearmm = ss[1]
sales = ss[2]
new_key = base_number - int(sales)
mm = yearmm[4:6]
print "%s\t%s\t%s" % (int(mm), int(new_key), fruit)
reduce_sort.py

 #!/usr/bin/python
import sys
base_number = 99999
for line in sys.stdin:
idx_id, sales, fruit = line.strip().split('\t')
new_key = base_number - int(sales)
print '\t'.join([val, str(new_key)])
执行脚本如下:
run.sh
 set -e -x
HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"
INPUT_FILE_PATH_A="/data/fruit.txt"
OUTPUT_SORT_PATH="/output_sort"
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_SORT_PATH
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH_A\
-output $OUTPUT_SORT_PATH \
-mapper "python map_sort.py" \
-reducer "python reduce_sort.py" \
-file ./map_sort.py \
-file ./red_sort.py \
-jobconf mapred.reduce.tasks= \
-jobconf stream.num.map.output.key.fields= \
-jobconf num.key.fields.for.partition= \
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner
-jobconf stream.num.map.output.key.fields=2 这行代码用于指定排序的字段,数字2指定map函数输出数据的第几列用于排序,就是例子中的sales字段。
-jobconf num.key.fields.for.partition=1这行代码指定partition字段,数字1指定map函数输出数据的第一列用于分区。
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner这行代码是调用hadoop streaming包中的分区类,实现分区功能。
 
实现streaming partition功能时这三行代码必不可少。
 
总结:
实现hadoop部分排序主要是通过partition方式实现。
java语言使用自定义分区Partition类实现分区的功能,而streaming是通过KeyFieldBasedPartitioner类,然后在脚本文件中指定partition类的方式实现。

一起学Hadoop——使用自定义Partition实现hadoop部分排序的更多相关文章

  1. 2 weekend110的hadoop的自定义排序实现 + mr程序中自定义分组的实现

    我想得到按流量来排序,而且还是倒序,怎么达到实现呢? 达到下面这种效果, 默认是根据key来排, 我想根据value里的某个排, 解决思路:将value里的某个,放到key里去,然后来排 下面,开始w ...

  2. Hadoop mapreduce自定义分区HashPartitioner

    本文发表于本人博客. 在上一篇文章我写了个简单的WordCount程序,也大致了解了下关于mapreduce运行原来,其中说到还可以自定义分区.排序.分组这些,那今天我就接上一次的代码继续完善实现自定 ...

  3. hadoop的自定义数据类型和与关系型数据库交互

    最近有一个需求就是在建模的时候,有少部分数据是postgres的,只能读取postgres里面的数据到hadoop里面进行建模测试,而不能导出数据到hdfs上去. 读取postgres里面的数据库有两 ...

  4. commoncrawl 源码库是用于 Hadoop 的自定义 InputFormat 配送实现

    commoncrawl 源码库是用于 Hadoop 的自定义 InputFormat 配送实现. Common Crawl 提供一个示例程序 BasicArcFileReaderSample.java ...

  5. Hadoop mapreduce自定义分组RawComparator

    本文发表于本人博客. 今天接着上次[Hadoop mapreduce自定义排序WritableComparable]文章写,按照顺序那么这次应该是讲解自定义分组如何实现,关于操作顺序在这里不多说了,需 ...

  6. 大数据学习笔记之Hadoop(一):Hadoop入门

    文章目录 大数据概论 一.大数据概念 二.大数据的特点 三.大数据能干啥? 四.大数据发展前景 五.企业数据部的业务流程分析 六.企业数据部的一般组织结构 Hadoop(入门) 一 从Hadoop框架 ...

  7. 《Hadoop》对于高级编程Hadoop实现构建企业级安全解决方案

    本章小结 ●    理解企业级应用的安全顾虑 ●    理解Hadoop尚未为企业级应用提供的安全机制 ●    考察用于构建企业级安全解决方式的方法 第10章讨论了Hadoop安全性以及Hadoop ...

  8. [BigData - Hadoop - YARN] YARN:下一代 Hadoop 计算平台

    Apache Hadoop 是最流行的大数据处理工具之一.它多年来被许多公司成功部署在生产中.尽管 Hadoop 被视为可靠的.可扩展的.富有成本效益的解决方案,但大型开发人员社区仍在不断改进它.最终 ...

  9. hadoop分布式存储(2)-hadoop的安装(毕业设计)

    总共分三步:1.准备linux环境 租用"云主机",阿里云,unitedStack等,云主机不受本机性能影响(或者直接安转linux操作系统或者虚拟机也行): PuTTy Conf ...

随机推荐

  1. FHQ Treap摘要

    原理 以随机数维护平衡,使树高期望为logn级别 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 因此可持久化 先介绍变量 ; int n; struct Node { int v ...

  2. 如何:配置 ClickOnce 信任提示行为

    转载链接:https://technet.microsoft.com/zh-cn/magazine/ee308453 可以配置 ClickOnce 信任提示以控制是否允许最终用户选择安装 ClickO ...

  3. 51nod--1298 (计算几何基础)

    题目: 1298 圆与三角形 题目来源: HackerRank 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 给出圆的圆心和半径,以及三角形的三个顶点,问圆 ...

  4. reportNG定制化之失败截图及日志

    先从github上拉下 reportNg的源代码 reportng  拉下源码后我们使用IDEA进行导入 1.reportng.properties 增加部分类表项 这里我们直接在末尾添加 log=L ...

  5. 如何在同一台电脑上使用两个github账户(亲测有效)

    1 前言 由于有两个github账号,要在同一台电脑上同步代码,需要给每一个账号添加一个SSH public key,此时推送时git push origin,不知道是哪个账号的远程仓库名称,所以需要 ...

  6. 14)django-模板(计数器)

    模块中for循环自带计数器. 使用场景:表格数据相增加序列号. 问:可以使用表的自增加序列做为序列号,但是这个存在个问题,即表中数据被删除,则会出现序列不连续. 1)每个循环都有6个公共方法,双循环有 ...

  7. 6)django-示例(fbv)

    FBV(function base view),即一个url对应views.py一个函数 示例演示如下 1)FBV如何使用 2)渲染页面,并返回字典数据 3)字典数据页面如何访问 1)url.py f ...

  8. Android 各种路径详细说明

    存储分类: 内部存储路径, 内部缓存存储路径, 外部存储路径, 外部缓存存储路径 在有些手机上内部划出一个内部的sdcard路径和内部存储路径,当有sdcard时候,就有了六个路径 内部存储空间中的应 ...

  9. 洛谷P3345 [ZJOI2015]幻想乡战略游戏 [动态点分治]

    传送门 调了两个小时,终于过了-- 凭啥人家代码80行我180行啊!!! 谁叫你大括号换行 谁叫你写缺省源 思路 显然,补给点所在的位置就是这棵树的带权重心. 考虑size已知时如何找重心:一开始设答 ...

  10. Vue项目构建开发笔记(vue-lic3.0构建的)

    1.router.js里面 { path: '/about', name: 'about', // route level code-splitting // this generates a sep ...