mapreduce
一.分区功能实现—partitoner分区逻辑书写
1.可以通过设置reduce Task的数量来实现运算结果的拆分
job.setNumReduceTasks()
此时,单独进行task数量的设置--->会按照自己的存储规则来进行数据的存储
- 例如:job.setNumReduceTasks(10);
可以看到,并不是每个文件都进行了运算结果的存储—>它会按照自己的存储规则进行存储
2.如何进行自己分区逻辑的书写?
-
需要用到 partitoner 分区
map到reduce进行数据分区,不同分区的数据写进对应的reduce中,进行数据合并
-
原理是 : 我们自己写分区的逻辑,执行代码时,会按照写的逻辑进行分区—>每个区中的数据分配到不同的reduce中执行===>这样不同reduce执行出来的数据就会存放在不同的文件中
-
以下为代码更新的部分
// 分区逻辑书写
// key是map KEYOUT的类型,value是map VALUEOUT的类型
public static class MyPar extends Partitioner<Text, Phone>{
@Override
public int getPartition(Text text, Phone phone, int i) {
// 如果phone的前三位等于123-->返回i=0
// 如果phone的前三位等于124-->返回i=1
// 如果phone的前三位等于125-->返回i=2
// 其他情况-->返回i=3
if (phone.getPhone().substring(0,3).equals("123")){
i=0;
}else if (phone.getPhone().substring(0,3).equals("124")){
i=1;
}else if (phone.getPhone().substring(0,3).equals("125")){
i=2;
}else{
i=3;
}
return i;
}
}
// job执行reduce的配置
job.setNumReduceTasks(4);
// 现在用的是自定义的partitoner--->不用默认的--->所以要进行类上的指定
job.setPartitionerClass(Demo6.MyPar.class);
完整代码
package neuedu.test;
import neuedu.pojo.Phone;
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.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.yarn.webapp.hamlet2.Hamlet;
import java.io.IOException;
public class Demo6 {
// 先继承Mapper
/**
* 四个范型
* (1)进行每一行的位置的记录:map读取指定文件中的数据——》每一行开始的位置,数字进行的记录——》我们现在写的是大数据,所以文件会比较大——》可以定义long类型(在大数据里面自己封装的叫做longWritable类型)
* (2)进行文件中数据的读取:map进行文件中数据的读取(按行读取),一行一行的进行数据获取—-》一行中数据为String类型(在大数据中国自己封装的类型叫做Text类型)
* (3)进行每一行单词的获取:map进行每一行单词的获取—-》要进行映射(单词,数量1)——》这里还要继续写出给reduce—-》需要key的类型(就是单词类型)—-》也就是字符串类型(大数据中为Text)
* (4)同(3):map写出到reduce中value的类型(数量1)—-》数字类型(先定义为int)—-》(大数据中为IntWritable)
* 注意:数量=1–》是因为我们现在写的简单逻辑是一行就看成是一个单词,所以读取一行,就赋值为1
*/
public static class Map1 extends Mapper<LongWritable, Text,Text, Phone> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String[] s=value.toString().split(" ");
int id=Integer.parseInt(s[0]);
String phone=(s[1]);
int up=Integer.parseInt(s[2]);
int down =Integer.parseInt(s[3]);
Phone phone1 = new Phone();
phone1.setId(id);
phone1.setPhone(phone);
phone1.setUp(up);
phone1.setDown(down);
context.write(new Text(phone),phone1);
}
}
// 分区逻辑书写
// key是map KEYOUT的类型,value是map VALUEOUT的类型
public static class MyPar extends Partitioner<Text, Phone>{
@Override
public int getPartition(Text text, Phone phone, int i) {
// 如果phone的前三位等于123-->返回i=0
// 如果phone的前三位等于124-->返回i=1
// 如果phone的前三位等于125-->返回i=2
// 其他情况-->返回i=3
if (phone.getPhone().substring(0,3).equals("123")){
i=0;
}else if (phone.getPhone().substring(0,3).equals("124")){
i=1;
}else if (phone.getPhone().substring(0,3).equals("125")){
i=2;
}else{
i=3;
}
return i;
}
}
// 先继承Reducer
/**
* 四个范型
* (1)map写入reduce中的数据,key类型—》Text
* (2)map写入reduce中的数据,value类型—》IntWritable
* (3)单词的类型:reduce进行数据合并,把数据写出到磁盘上,格式是(单词,数量)—》先写出单词的类型(key类型)—》字符串—〉Text
* (4)数量的类型:value类型—》LongWritable
*/
public static class Reduce1 extends Reducer<Text,Phone,Phone, NullWritable> {
/**
* key===>从map中接受的key的类型
* values===>是一个集合,进行当前可以相同时,数据值的存储
* reduce中的数据写出到hdfs上===>通过context进行的写出
*/
@Override
protected void reduce(Text key, Iterable<Phone> values, Context context) throws IOException, InterruptedException {
for (Phone p:values
) {
int sum=p.getUp()+p.getDown();
p.setSum(sum);
context.write(p,null);
}
}
}
public static void main(String[] args){
// 进行java程序的运行
// hdfs的配置
Configuration conf=new Configuration();
conf.set("fs.defaultFS","hdfs://192.168.246.177:9000");
try {
Job job = Job.getInstance(conf);
// job相关信息
// 设置job的名字
job.setJobName("javawordcount");
// job运行类
job.setJarByClass(Demo6.class);
// job执行map相关信息的配置
job.setMapperClass(Demo6.Map1.class);
// 向reduce输出key的类型
job.setMapOutputKeyClass(Text.class);
// 向rudece输出value的类型
job.setMapOutputValueClass(Phone.class);
// job执行reduce的配置
job.setReducerClass(Demo6.Reduce1.class);
job.setOutputKeyClass(Phone.class);
job.setOutputValueClass(NullWritable.class);
job.setNumReduceTasks(4);
// 现在用的是自定义的partitoner--->不用默认的--->所以要进行类上的指定
job.setPartitionerClass(Demo6.MyPar.class);
// 注意导包import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
FileInputFormat.setInputPaths(job,new Path("/phone.txt"));
FileOutputFormat.setOutputPath(job,new Path("/java04"));
// 提交执行
job.waitForCompletion(true);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
二.排序功能实现
1.TreeSet引入
- (1)简单的TreeSet
package neuedu.test;
import java.util.TreeSet;
public class test3 {
public static void main(String[] args){
TreeSet<String> sets=new TreeSet<>();
sets.add("fes");
sets.add("aef");
sets.add("ssg");
sets.add("fseg");
System.out.println(sets);//输出结果:[aef, fes, fseg, ssg]
TreeSet<Integer> set=new TreeSet<>();
set.add(1);
set.add(463);
set.add(43);
set.add(12);
set.add(654);
System.out.println(set);//输出结果:[1, 12, 43, 463, 654]
}
}
- (2)如果创建一个pojo对象,可不可以进行排序呢?
- pojo类–>Stu
package neuedu.pojo;
import lombok.Data;
@Data
public class Stu {
private String name;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
private int age;
}
- 测试
TreeSet<Stu> stus = new TreeSet<>();
stus.add(new Stu("张三",12));
stus.add(new Stu("李四",34));
stus.add(new Stu("王五",00));
stus.add(new Stu("赵六",45));
stus.add(new Stu("宋七",11));
System.out.println(stus);
}
测试之后会发现报错:Exception in thread "main" java.lang.ClassCastException: neuedu.pojo.Stu cannot be cast to java.lang.Comparable
因为用TreeSet进行排序需要继承comparable,书写compareTo逻辑
String和int可以使用TreeSet是因为String类和int类中有compareTo逻辑
而我们自己创建的pojo类还没有compareTo逻辑--->所以会报错,不能排序
- 在Stu中添加CompareTo
package neuedu.pojo;
import lombok.Data;
@Data
public class Stu implements Comparable<Stu>{
private String name;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
private int age;
@Override
public int compareTo(Stu o) {
return this.age-o.age;//按照年龄的升序排序
// return -(this.age-o.age);//按照年龄的降序排序
}
}
这时测试,输出结果为[Stu(name=王五, age=0), Stu(name=宋七, age=11), Stu(name=张三, age=12), Stu(name=李四, age=34), Stu(name=赵六, age=45)]
2.对phone.txt文件进行排序实现
(1)写compareTo逻辑
- WritableComparable 既能序列化,也能作数据的比较
- pojo类–Phone.java
package neuedu.pojo;
import lombok.Data;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@Data
//public class Phone implements Writable,Comparable<Phone> {
public class Phone implements WritableComparable<Phone> {
private int id;
private String phone;
private int up;
private int down;
private int sum;
private int status;
@Override
public String toString() {
return phone+'\t'+up+'\t'+down+'\t'+sum;
}
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeInt(this.id);
dataOutput.writeUTF(this.phone);
dataOutput.writeInt(this.up);
dataOutput.writeInt(this.down);
dataOutput.writeInt(this.status);
dataOutput.writeInt(this.sum);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
this.id=dataInput.readInt();
this.phone=dataInput.readUTF();
this.up=dataInput.readInt();
this.down=dataInput.readInt();
this.status=dataInput.readInt();
this.sum=dataInput.readInt();
}
@Override
public int compareTo(Phone o) {
return this.up-o.up;
}
}
23:22视频