Hadoop二次排序
来源:互联网 发布:mac录音精灵破解版 编辑:程序博客网 时间:2024/06/05 00:13
二次排序
前言
Hadoop的map和reduce阶段默认用Key值作为记录排序的依据,如果想按照Value值或其他自定义的方式进行排序,就需要使用Hadoop提供的机制来实现所谓的”二次排序”。
这篇文章涉及到的概念有:
- 自定义Key
- 自定义分区规则
- 自定义排序规则
- 自定义分组规则
实验环境
操作系统:Ubuntu 16.04 LTS
Hadoop版本:Apache Hadoop2.6.5
JDK:JDK1.7
问题描述:
有如下的数据,要求:
同龄的数据分为一组,组内按身高升序排列。
注:左列为“年龄”,右列为“身高”。这里忽略数据合理性。
问题分析
map()输出中,以“年龄”作为Key,“身高”作为value输出。
四种年龄值:12,13,14,15,直接设置reducer个数为4。
不同年龄的数据送至不同的Reducer。
自定义的排序类,实现自定义排序逻辑,这里按“身高”进行升序排列。
在下面的解决方法中,使用自定义的Key类KeyPair.java
,将“年龄 身高”组合为一个新的对象,这是为了体现自定义Key的机制,与本问题无关。
编码
项目结构:
KeyPair.java
自定义Key类,实现WritableComparable接口,作为被比较的对象。
package mr;import java.io.DataInput;import java.io.DataOutput;import java.io.IOException;import org.apache.hadoop.io.WritableComparable;public class KeyPair implements WritableComparable<KeyPair> { private int age; private int height; public KeyPair(int age, int height) { super(); this.age = age; this.height = height; } public KeyPair() { super(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } //反序列化 @Override public void readFields(DataInput in) throws IOException { this.age = in.readInt(); this.height = in.readInt(); } //序列化 @Override public void write(DataOutput out) throws IOException { out.writeInt(age); out.writeInt(height); } //进行排序时的依据,如果没有通过 //job.setSortComparatorClass(XXX.class)设置比较类,则默认用compareTo作为比较大小 @Override public int compareTo(KeyPair o) { return Integer.compare(age, o.getAge()); } @Override public String toString() { // TODO Auto-generated method stub return age+" "+height; }}
MyPartition .java
自定义分区规则
package demo;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Partitioner;import mr.KeyPair;public class MyPartition extends Partitioner<KeyPair, Text>{ //白话:指定map_task产生的输出分别到哪个reduce_task中去 //num即为reduce个数,这里输出分段刚好被分配到num个reduce_task中 @Override public int getPartition(KeyPair key, Text value, int num) { return (key.getAge() % num); }}
SortAge.java
自定义排序规则
package demo;import org.apache.hadoop.io.WritableComparable;import org.apache.hadoop.io.WritableComparator;import mr.KeyPair;public class SortAge extends WritableComparator { public SortAge() { super(KeyPair.class,true); } //自定义排序规则 //这里的规则为:按年龄升序排列,同龄时按身高升序排列 public int compare(WritableComparable a, WritableComparable b) { KeyPair first=(KeyPair)a; KeyPair second=(KeyPair)b; int res= Integer.compare(first.getAge(), second.getAge()); if(0==res){ return Integer.compare(first.getHeight(), second.getHeight()); } return res; }}
MyGroup.java
自定义Reducer端的分组规则
package demo;import org.apache.hadoop.io.WritableComparable;import org.apache.hadoop.io.WritableComparator;import mr.KeyPair;public class MyGroup extends WritableComparator { public MyGroup() { super(KeyPair.class,true); } //指定分组规则,reduce端执行,根据map-reduce语义,reduce端先将key相同的记录group, // 生成<key,interable<>>迭代形式,传递给reduce函数 //这里按照年龄进行分组,只要keyPair对象中的age一致,就认为是一个组 //WritableCimparable即为自定义的KeyPair public int compare(WritableComparable a, WritableComparable b) { KeyPair first=(KeyPair)a; KeyPair second=(KeyPair)b; return Integer.compare(first.getAge(), second.getAge()); }}
关于分组的一些疑惑:
这里以age作为分组依据,age相同的记录作为同一个分组,难道<10:112 “MAP”>和<10:58 “MAP”>能作为一个分组进行“合并”吗? “合并”后的形式是什么样的?
【问题概括】
如果是<10 “MAP”>与<10 “DOW”>这两个记录
Key值都为10,value值不相同,合并为<10,<”MAP”,”DOW”>>这样的形式。如果是<10 “MAP”>与<11 “DOW”>这两个记录,业务逻辑上要求分为一组,能合并吗?Key值不同怎么合并成
<Key key, Iterable<Text> value>
的形式?关于Reduce端分组的问题,网上很多资料语焉不详,或以讹传讹。
这篇博文解答了我的疑惑,大家感兴趣可以阅读
hadoop的mapreduce编程模型中GroupingComparator的使用 - 黎杰的博客 - CSDN博客
MyMapper
map()函数,读入每行记录,按空白划分记录,生成KeyPair对象,作为map输出的Key。
package mr;import java.io.IOException;import org.apache.hadoop.io.LongWritable;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Mapper;import mr.KeyPair;public class MyMapper extends Mapper<LongWritable, Text, KeyPair, Text> { public void map(LongWritable ikey, Text ivalue, Context context) throws IOException, InterruptedException { String[] line = ivalue.toString().split("\\s+"); if (line.length == 2) { int age = Integer.parseInt(line[0]); int height = Integer.parseInt(line[1]); //这里的new Text("MAP")仅仅是测试,填补一个Text context.write(new KeyPair(age, height), new Text("MAP")); } }}
MyReducer.java
reduce()函数,这里输入为<KeyPair key, Iterable<Text> value>
package mr;import java.io.IOException;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Reducer;public class MyReducer extends Reducer<KeyPair, Text, KeyPair, Text> { @Override protected void reduce(KeyPair key, Iterable<Text> value, Context context) throws IOException, InterruptedException { for (Text v : value) { context.write(key, v); } }}
Driver .java
驱动类,配置job,提交作业
package mr;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Job;import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;import demo.MyGroup;import demo.MyPartition;import demo.SortAge;public class Driver { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "AGE"); job.setJarByClass(mr.Driver.class); job.setMapperClass(MyMapper.class); job.setReducerClass(MyReducer.class); //测试集中年龄种类数为4,将每个年龄的数据划分给一个reduce_task处理 job.setNumReduceTasks(4); //指定排序类,在map_task和rudece_task端都会执行 job.setSortComparatorClass(SortAge.class); //指定分组类,reduce_task执行 job.setGroupingComparatorClass(MyGroup.class); //指定分区类:数据被发送到哪个recude_task进行处理,map_task端执行 job.setPartitionerClass(MyPartition.class); //设定reduce输出Key,Value的格式,这里KeyPair为自定义的Key job.setOutputKeyClass(KeyPair.class); job.setOutputValueClass(Text.class); FileInputFormat.setInputPaths(job, new Path("/user/root/input/test.txt")); FileOutputFormat.setOutputPath(job, new Path("/user/root/output_test")); if (!job.waitForCompletion(true)) return; }}
结果
如图,四组年龄共输出四份文件,每份年龄相同,按身高升序排列(“MAP”为测试用的Text,无意义)
反思
这个例子虽然简单,但囊括了map-reduce计算模型中几个非常重要的组成部分:如自定义Key,自定义分区规则,自定义排序规则,自定义分组规则等。
这里值得反思的是,对于上面的每个自定义的类,要能说明白:
- 为什么要定义这个类?
- 怎么做的?
- 在哪个节点做的?
- 在什么时候做的?
其实这些都是Map-Reduce计算模型中非常基本的内容,但自己往往犯了迷糊。
下一篇打算总结下Shuffle各个阶段的运行原理,加深自己的认识,把基础搞扎实。
参考资料
彻底理解MapReduce shuffle过程原理 - ArmandXu的专栏 - CSDN博客
Hadoop学习笔记—11.MapReduce中的排序和分组 - Edison Chou - 博客园
Hadoop学习笔记—10.Shuffle过程那点事儿 - Edison Chou - 博客园
刘超:MapReduce二次排序
hadoop的mapreduce编程模型中GroupingComparator的使用 - 黎杰的博客 - CSDN博客
- hadoop 二次排序
- Hadoop二次排序
- Hadoop二次排序
- Hadoop二次排序
- hadoop二次排序
- hadoop之二次排序
- hadoop二次排序一
- hadoop二次排序二
- hadoop二次排序三
- Hadoop二次排序
- hadoop的二次排序
- Hadoop二次排序<转>
- Hadoop二次排序
- Hadoop二次排序
- hadoop二次排序
- hadoop 二次排序
- hadoop二次排序<转>
- Hadoop二次排序<转>
- Spring的 classpath 通配符加载配置文件
- Python3.5.4对应版本matplotlib的安装过程(含NumPy&SciPy)
- MATLAB字符拼接小技巧
- MySQL表结构(含数据类型、字段备注注释)导出成Excel
- 7 什么是GAN(生成对抗网络)?
- Hadoop二次排序
- mac系统 安装 JDK 并配置环境
- 单机MySQL多实例安装
- Sql Server 配置IP地址连接
- Eclipse更新SVN版本
- Android热更新——Tinker 使用
- 文章标题
- 只是觉得有点儿意思 值得琢磨下
- python 中文乱码