一.实验原理
MR默认会对键进行排序,然后有的时候我们也有对值进行排序的需要,如果有内存溢出的问题,就用二次排序来进行对值的排序MR计算过程中,而不是单独来做。
二次排序就是首先按照第一次字段排序,然后对第一字段相同的行按照第二字段排序,注意不能破坏第一次排序结果。
二.实验需要
本实验所需外部 jar 文件都可以在实验集群的/home/hadoop/lib.zip 文件中找到,请自行下载。
1.编写代码
这次我们需要创建两个类
一个是用于来数据的存储
一个是以实现第一字段和第二字段的比较。
IntPair类:
package mr;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.WritableComparable;
public class IntPair implements WritableComparable<IntPair> {
private IntWritable first;
private IntWritable second;
public void set(IntWritable first, IntWritable second) {
this.first = first;
this.second = second;
}
//注意:需要添加无参的构造方法,否则反射时会报错。
public IntPair() {
set(new IntWritable(), new IntWritable());
}
public IntPair(int first, int second) {
set(new IntWritable(first), new IntWritable(second));
}
public IntPair(IntWritable first, IntWritable second) {
set(first, second);
}
public IntWritable getFirst() {
return first;
}
public void setFirst(IntWritable first) {
this.first = first;
}
public IntWritable getSecond() {
return second;
}
public void setSecond(IntWritable second) {
this.second = second;
}
public void write(DataOutput out) throws IOException {
first.write(out);
second.write(out);
}
public void readFields(DataInput in) throws IOException {
first.readFields(in);
second.readFields(in);
}
public int hashCode() {
return first.hashCode() * 163 + second.hashCode();
}
public boolean equals(Object o) {
if (o instanceof IntPair) {
IntPair tp = (IntPair) o;
return first.equals(tp.first) && second.equals(tp.second);
}
return false;
}
public String toString() {
return first + "\t" + second;
}
public int compareTo(IntPair tp) {
int cmp = first.compareTo(tp.first);
if (cmp != 0) {
return cmp;
}
return second.compareTo(tp.second);
}
}
SecondarySort类
完整代码:
package mr;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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;
public class SecondarySort {
static class TheMapper extends Mapper<LongWritable, Text, IntPair, NullWritable>
{
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] fields = value.toString().split("\t");
int field1 = Integer.parseInt(fields[0]);
int field2 = Integer.parseInt(fields[1]);
context.write(new IntPair(field1,field2), NullWritable.get());
}
}
static class TheReducer extends Reducer<IntPair, NullWritable,IntPair,
NullWritable> {
//private static finalTextSEPARATOR=newText("------------------------------------------------");
@Override
protected void reduce(IntPair key, Iterable<NullWritable> values, Context
context)
throws IOException, InterruptedException {
context.write(key, NullWritable.get());
}
}
public static class FirstPartitioner extends Partitioner<IntPair, NullWritable> {
public int getPartition(IntPair key, NullWritable value,
int numPartitions) {
return Math.abs(key.getFirst().get()) % numPartitions;
}
}
//如果不添加这个类,默认第一列和第二列都是升序排序的。
//这个类的作用是使第一列升序排序,第二列降序排序
public static class KeyComparator extends WritableComparator {
//无参构造器必须加上,否则报错。
protected KeyComparator() {
super(IntPair.class, true);
}
public int compare(WritableComparable a, WritableComparable b) {
IntPair ip1 = (IntPair) a;IntPair ip2 = (IntPair) b;
//第一列按升序排序
int cmp = ip1.getFirst().compareTo(ip2.getFirst());
if (cmp != 0) {
return cmp;
}
//在第一列相等的情况下,第二列按倒序排序
return -ip1.getSecond().compareTo(ip2.getSecond());
}
}
//入口程序
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(SecondarySort.class);
//设置 Mapper 的相关属性
job.setMapperClass(TheMapper.class);
//当 Mapper 中的输出的 key 和 value 的类型和 Reduce 输出
//的 key 和 value 的类型相同时,以下两句可以省略。
//job.setMapOutputKeyClass(IntPair.class);
//job.setMapOutputValueClass(NullWritable.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
//设置分区的相关属性
job.setPartitionerClass(FirstPartitioner.class);
//在 map 中对 key 进行排序
job.setSortComparatorClass(KeyComparator.class);
//job.setGroupingComparatorClass(GroupComparator.class);
//设置 Reducer 的相关属性
job.setReducerClass(TheReducer.class);
job.setOutputKeyClass(IntPair.class);
job.setOutputValueClass(NullWritable.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//设置 Reducer 数量
int reduceNum = 1;
if(args.length >= 3 && args[2] != null){
reduceNum = Integer.parseInt(args[2]);
}
job.setNumReduceTasks(reduceNum);
job.waitForCompletion(true);
}
}
导入到idea我遇到了2个错误问题:
1.SLF4J-jar包多绑定冲突
从提示信息来看,是hadoop的slf4j 与hbase的slf4j jar包发生了冲突,移除其中一个即可需要删除一个重复类型的jar包。
***2.数组越界:***
Java 83行 FileInputFormat.setInputPaths(job, new Path(args[0]));
因为我们整个java类没有一个数组的数据只有一个框架结构,需要添加数据进入java类中,但是我们用xftp导入一个数组的.txt的文件,所以这里报错的问题,就调用hadoop jar包方法来解决。
打包提交 打包并上传到Ubantu hadoop 中
使用idea 开发工具将代码打包,选择主类为mr.Counters. 假定打包后的文件为Counters.jar。将打包好的用Xftp导入**/usr/local/hadoop/share/hadoop/里并应用。
在windnows 系统 创建 一个secsortdata.txt** 内容:
7 444
3 9999
7 333
8
4 22
3 7777
7 555
3 6666
6 0
3 8888
4 11
将secsortdata.txt 文件用Xftp上传至/home/主目录下,这样好查询。
登录hadoop系统
sbin/start-all.sh 启动hadoop
Jps 查询进程 5个进程就是启动成功!
输入数据查询结果
创建文件夹 :
Hadooop fs -mkdir -p /user/mapreduce/secsort/in/
查询是否创建成功:
Hadoop fs -ls /user/mapreduce/secsort/in/
将secsortdata.txt传入hdfs系统中
Hadoop fs -put /home/hadoop/secsortdata.txt /user/mapreduce/secsort/in/
查询是否上传成功:
Hadoop fs -ls /user/mapreduce/secsort/in/
运用jar执行secsortdata.txt 执行结果。
Hadoop jar SecondarySotr.jar /user/mapreduce/secsort/in/secsortdata.txt /user/mapreduce/secsort/out
我在运行途中遇到了2个错误:
警告mapred。LocalJobRunner: job_local635467211_0001. lang。例外:. lang。NumberFormatException:用于输入字符串:“7 444”
这里警告不能用\t这些html5标签或者用“\t”;但是还是会继续运行下去。
****第二个问题:把txt导入到文件里的时候,会提示hdfs:数据源异常问题。
最终在官网上找到答案:这是native库不兼容造成的(本人尝试过替换native库),目前算是Hadoop的一个bug,还没有解决方案,可以忽略掉。
但是hadoop fs -ls/user/mapreduce/secsort/in/secsortdata.txt 能查询到txt文本已经上传成功。
查询结果:hadoop fs -cat /user/mapreduce/secsort/out/p*****
二次排序就成功了,在学习中遇到了一些问题,也在逐渐解决问题。加油!