day05 hadoop(shuffle优化,zk搭建Hadoop)

三、shuffle的优化
1.减少溢写次数︰
a.增大缓冲区,实际过程中缓冲区的大小一般是在250~400M之间b.增大缓冲区阈值,同时增加了写入阻塞的风险–不建议
c.增加Combine的过程
2.可以考虑将Map的结果文件进行压缩,这个方案是在网络资源和CPU资源之
间的取舍
3.增加fetch线程的数量4.增大merge因子

InputFormat(格式处理【处理一行变处理多行】)

一、概述
1.InputFormat中定义了2个抽象方法∶
a. getSplits用于产生切片
b. createRecordReader产生输入流读取切片
2. InputFormat会把结果给到MapTask

在这里插入图片描述
在这里插入图片描述
3.实际过程中,如果需要自定义输入格式类,一般不是直接继承InputFormat而是继承它的子类FileInputFormat,这个子类中已经覆盖了getSplits方法,而只需要考虑如何读取数据即可
4.如果没有指定输入格式,那么默认使用的TextInputFormat。除了第一个切
片对应的MapTask意外,其余的MapTask都是从当前切片的第二行开始读取到下一个切片的第一个行【因为数据的完整性,读数据要读完整的,切则按规定的切】

案例:

package cn.tedu.authinput;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class AuthDriver {

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf, "JobName");
		job.setJarByClass(cn.tedu.authinput.AuthDriver.class);
		job.setMapperClass(AuthMapper.class);
		job.setReducerClass(AuthReducer.class);
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(Score.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		
		//指定输入格式
		job.setInputFormatClass(AuthInputFormat.class);
		FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.253.129:9000/mr/score4/score4.txt"));
		FileOutputFormat.setOutputPath(job, new Path("hdfs://192.168.253.129:9000/result/score4"));

		if (!job.waitForCompletion(true))
			return;
	}

}

package cn.tedu.authinput;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.util.LineReader;

public class AuthInputFormat extends FileInputFormat<Text, Text>{
	
	@Override
	public RecordReader<Text, Text> createRecordReader(InputSplit arg0, TaskAttemptContext arg1)
			throws IOException, InterruptedException {
		return new AuthReader();
	}

}
class AuthReader extends RecordReader<Text,Text>{
	private LineReader reader;
	private Text key=new Text();
	private Text value=new Text();
	private static final Text blank=new Text(" ");
	//初始化
	//在这个初始化方法中先获取到流
	@Override
	public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
		//从切片中确定要读取的文件
		FileSplit fSplit=(FileSplit) split;
		//获取文件路径
		Path p=fSplit.getPath();
		//连接hdfs
		FileSystem fs=FileSystem.get(URI.create(p.toString()),context.getConfiguration() );
		//获取到针对文件的输入流
		InputStream in = fs.open(p);
		//将字节流转化为字符流----这个字符流最好能按行读
		reader=new LineReader(in);
		
	}
	
	//读取文件
	//如果读到了,则表示还有键和值需要处理
	//如果没有读到,则表示已经没有数据了
	@Override
	public boolean nextKeyValue() throws IOException, InterruptedException {
		//按照当前说法,这个方法,只需要试着读取三行
		//如果读到了三行表示有数据需要处理,需要返回true
		
		Text tmp=new Text();
		//表示会将读到的这一行数据放入传的Text参数中
		// readLine方法返回值表示读取的这一行的字节数
		if(reader.readLine(tmp)==0)
			return false;
		//读取完第一行,需要将第一行的数据作为键来使用
		key.set(tmp.toString());
		//第二行和第三行拼接作为值来使用
		if(reader.readLine(tmp)==0)
			return false;
		value.set(tmp.toString());
		value.append(blank.getBytes(), 0, blank.getLength());
		if(reader.readLine(tmp)==0)
			return false;
		value.append(tmp.getBytes(), 0, tmp.getLength());
		return true;
	}
	@Override
	public Text getCurrentKey() throws IOException, InterruptedException {
		return key;
	}

	@Override
	public Text getCurrentValue() throws IOException, InterruptedException {
		return value;
	}
	//获取执行进度
	@Override
	public float getProgress() throws IOException, InterruptedException {
		return 0;
	}
	
	@Override
	public void close() throws IOException {
		if(reader!=null)
			reader.close();
		key=null;
		value=null;
	}

}

package cn.tedu.authinput;

import java.io.IOException;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class AuthMapper 
extends Mapper<Text, Text, Text, Score> {

	public void map(Text key, Text value, Context context) throws IOException, InterruptedException {
		
		//key tom 
		//value math 100 english 100 
		String[] arr=value.toString().split(" ");
		Score s=new Score();
		s.setMath(Integer.parseInt(arr[1]));
		s.setEnglish(Integer.parseInt(arr[3]));
		context.write(key, s);
	}

}

package cn.tedu.authinput;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class AuthReducer extends Reducer<Text, Score, Text, IntWritable> {

	public void reduce(Text key, Iterable<Score> values, Context context) throws IOException, InterruptedException {
		int sum=0;
		for (Score val : values) {
			sum=val.getMath()+val.getEnglish();
		}
		context.write(key, new IntWritable(sum));
	}

}

package cn.tedu.authinput;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.Writable;

public class Score implements Writable{
	private int math;
	private int english;
	public int getMath() {
		return math;
	}
	public void setMath(int math) {
		this.math = math;
	}
	public int getEnglish() {
		return english;
	}
	public void setEnglish(int english) {
		this.english = english;
	}
	@Override
	public String toString() {
		return "Score [math=" + math + ", english=" + english + "]";
	}
	@Override
	public void readFields(DataInput in) throws IOException {
		this.math=in.readInt();
		this.english=in.readInt();
	}
	@Override
	public void write(DataOutput out) throws IOException {
		out.writeInt(math);
		out.writeInt(english);
	}
	
}

5.多源输入下,允许输入不同格式的文件,但是文件格式可以不同Mapper类也可以不一样,但是最后交给Reducer处理的时候要一样(import org.apache.hadoop.mapreduce.lib.input.MultipleInputs;)

自定义输出文件名
conf.set("mapreduce.output.basename", "auth");
设置输出文件key  和  value之间的连接符,迷人是制表符
conf.set( "mapreduce.output.textoutputformat.separator","+++");

数据倾斜
一、概述
1.在开发MR程序时,可能遇到的数据分配不均匀,造成程序性能下降的问题,这个问题称之为数据倾斜问题
二、解决方案
1.如果是因为shuffle分配数据不均匀造成数据倾斜,重写parition均匀分配数据即可
2.如果是数据本身带有倾斜的特点,无法通过修改parition来解决倾斜问题,可以采取如下方案进行:
a.利用combiner减轻倾斜的情况
b.将造成倾斜的数据拿出单独处理
c.将—个mr拆分成多个mr降低倾斜造成的危害
d.多表联查

一、数据倾斜
1.数据本身就有倾斜特性,即日常生活中所产生的数据本身就是不均等的2实际过程中,绝大部分的数据倾斜都会产生在Reduce端
3. Map端产生倾斜的条件:多源输入、文件不可切且文件大小不均,map端的倾斜一旦产生,无法解决----如果真要解决,在特定条件下可以考虑缓存存根问题。
4. Reduce端的倾斜的本质是因为数据的倾斜性,但是直观原因是因为对数据进行了分类-分类规则往往是不可变的,所以在实际过程中往往考虑的是使用两阶段聚合–数据先打散再聚合

在这里插入图片描述
在这里插入图片描述
二阶段聚合
在这里插入图片描述

5000a5*1000a
600A5*120A
1500+5*300+
在这里插入图片描述

扩展:
##map任务数据倾斜原理分析
对于map/reduce任务,数据倾斜一般出现在reduce阶段,后文在将对其进行着重分析,但map过程同样会出现数据倾斜。
map过程产生数据倾斜的原因只有一个——map任务读取不支持sp)littable的原始文件且原始文件大小不均匀,有个别文件特别大。比如部分文件大小为2G,部分文件只有2K,假设数据处理时间和文件大小成正比,那么处理2G大小文件的task的时间是2K大小文件的100万倍。
这里先解释一下“splittable”。以HDFS分帮式存储系统为例,splittable指的是一个文件是否可以被多个map同时读取,每个map读取文件的一部分数据。对于支持splittable的文件,若干文件由N个block组成,那么其可以被N个map任务同时处理,每个map处理一个block的数据,因此不管单个文件大小是多少,这种支持splittable的文件都不会产生数据倾斜。

##reduce任务数据倾斜产生场景
对于给定的未知数据,如果预先对数据特点进行分析,那么很容易发现所有可能产生数据倾斜的key,这也是本文在一开头就强调的处理大数据任务时要“优先分析数据特点”。那么现实生产环境中哪些场景下key可能会倾斜呢?根据我们的经验,主要有两大类场景︰
1)存在业务默认填充值【性别单选框默认是男或者女,默认填充】
如用户的imei在获取不到时被填充了默认值;如广告系统在请求不到广告时播放了默认的广告,这些默认的广告的订单号都相同;如某个业务字段当前只有一个可选值。
2)业务本身存在热点
如热播剧的广告曝光量会显著大于一般的剧;视频前贴片这个广告位类型的曝光量会显著大于其他广告位类型;国内的广告曝光量显著大于国外。
3)存在恶意数据
如同一个ID刷了海量广告曝光。

两阶段聚合案例

package cn.tedu.join;



import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
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;

public class JoinDriver {

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf, "JobName");
		job.setJarByClass(cn.tedu.join.JoinDriver.class);
		job.setMapperClass(JoinMapper.class);
		job.setReducerClass(JoinReducer.class);
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(Order.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(DoubleWritable.class);

		//将小文件缓存,处理大文件
		URI[] uri={URI.create("hdfs://192.168.253.129:9000/mr/union/product.txt")};
		job.setCacheFiles(uri);
		//输入路径中给定的应该是大文件
		FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.253.129:9000/mr/union/order.txt"));
		FileOutputFormat.setOutputPath(job, new Path("hdfs://192.168.253.129:9000/result/unionprice"));

		if (!job.waitForCompletion(true))
			return;
	}

}

package cn.tedu.join;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;


public class JoinMapper extends Mapper<LongWritable, Text, Text, Order> {

	private Map<String, Order> map=new HashMap<>();
	
	//在处理大文件的时候需要小文件中的数据
	//那也就意味着在处理大文件之前需要先把小文件解析
	//小文件解析一次之后放入内存中供我们进行查询
	
	@Override
	protected void setup(Mapper<LongWritable, Text, Text, Order>.Context context)
			throws IOException, InterruptedException {
		//先将小文件从缓存中取出来
		URI file = context.getCacheFiles()[0];
		//连接hdfs 读取小文件
		FileSystem fs=FileSystem.get(file, context.getConfiguration());
		//获取针对这个文件的输入流
		InputStream in=fs.open(new Path(file.toString()));
		//考虑将字节流转化为字符流---最好能按行读取
		BufferedReader reader=new BufferedReader(new InputStreamReader(in));
		String line;
		while((line=reader.readLine())!=null){
			//1 chui 3999
			String[] arr=line.split(" ");
			Order o=new Order();
			o.setProid(arr[0]);
			o.setName(arr[1]);
			o.setPrice(Double.parseDouble(arr[2]));
			map.put(o.getProid(), o);
			//System.out.println(map);
		}
		reader.close();
	}
	public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
		// 1001 20170710 4 2
		String[] arr = value.toString().split(" ");
		Order o = new Order();
		o.setOrderid(arr[0]);
		o.setDate(arr[1]);
		o.setProid(arr[2]);
		o.setNum(Integer.parseInt(arr[3]));
		o.setName(map.get(o.getProid()).getName());
		o.setPrice(map.get(o.getProid()).getPrice());
		context.write(new Text(o.getOrderid()),o);

	}

}

package cn.tedu.join;

import java.io.IOException;

import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class JoinReducer extends Reducer<Text, Order, Text, DoubleWritable> {

	public void reduce(Text key, Iterable<Order> values, Context context) throws IOException, InterruptedException {
		double sum=0;
		for (Order val : values) {
			sum=val.getNum()*val.getPrice();
		}
		context.write(key, new DoubleWritable(sum));
	}

}

package cn.tedu.join;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.Writable;

public class Order implements Writable{
	private String orderid;//商品id
	private String date;
	private String proid;//商品编号
	private int num;//数量
	private String name;
	private double price;//单价
	public String getOrderid() {
		return orderid;
	}
	public void setOrderid(String orderid) {
		this.orderid = orderid;
	}
	public String getDate() {
		return date;
	}
	public void setDate(String date) {
		this.date = date;
	}
	public String getProid() {
		return proid;
	}
	public void setProid(String proid) {
		this.proid = proid;
	}
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	@Override
	public String toString() {
		return "Order [orderid=" + orderid + ", date=" + date + ", proid=" + proid + ", num=" + num + ", name=" + name
				+ ", price=" + price + "]";
	}
	@Override
	public void readFields(DataInput in) throws IOException {
		this.orderid=in.readUTF();
		this.date=in.readUTF();
		this.proid=in.readUTF();
		this.num=in.readInt();
		this.name=in.readUTF();
		this.price=in.readDouble();
	}
	@Override
	public void write(DataOutput out) throws IOException {
		out.writeUTF(orderid);
		out.writeUTF(date);
		out.writeUTF(proid);
		out.writeInt(num);
		out.writeUTF(name);
		out.writeDouble(price);
		
	}
	
}

二、小文件
1.小文件的危害∶
a.存储:大量小文件会产生大量的元数据,就导致内存被大量占用
b.计算:大量小文件就产生大量的切片,大量切片则意味着有大量的MapTask,会导致服务器的执行效率变低甚至会导致服务器崩溃
2.针对小文件的处理手段常见的有2种∶合并和压缩
Hadoop提供了一种原生的合并手段:Hadoop Archive,将多个小文件打成一个har包

合并txt目录下的所有小文件到根目录下的result下,名命为txt.har
hadoop archive -archiveName txt.har -p /txt /result 

案例:隐藏好友

package cn.tedu.friend;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class FriendDriver {

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf, "JobName");
		job.setJarByClass(cn.tedu.friend.FriendDriver.class);
		job.setMapperClass(FriendMapper.class);
		job.setReducerClass(FriendReducer.class);
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(Text.class);

		FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.253.129:9000/result/friend/"));
		FileOutputFormat.setOutputPath(job, new Path("hdfs://192.168.253.129:9000/result/friend2"));

		if (!job.waitForCompletion(true))
			return;
	}

}

package cn.tedu.friend;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class FriendMapper extends Mapper<LongWritable, Text, Text, IntWritable> {

	public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
		String[] arr=value.toString().split("\t");
		context.write(new Text(arr[0]), new IntWritable(Integer.parseInt(arr[1])));
	}

}

package cn.tedu.friend;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class FriendReducer extends Reducer<Text, IntWritable, Text, Text> {

	public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
		// process values
		for (IntWritable val : values) {
			if(val.get()==1)
				return;
		}
		String[] arr=key.toString().split("-");
		context.write(new Text(arr[0]), new Text(arr[1]));
	}

}

package cn.tedu.friend;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class RelationDriver {

	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf, "JobName");
		job.setJarByClass(cn.tedu.friend.RelationDriver.class);
		job.setMapperClass(RelationMapper.class);
		job.setReducerClass(RelationReducer.class);
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(Text.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);

		FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.253.129:9000/mr/friend/friend.txt"));
		FileOutputFormat.setOutputPath(job, new Path("hdfs://192.168.253.129:9000/result/friend"));

		if (!job.waitForCompletion(true))
			return;
	}

}

package cn.tedu.friend;

import java.io.IOException;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class RelationMapper extends Mapper<LongWritable, Text, Text, Text> {

	public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
		String[] arr=value.toString().split(" ");
		context.write(new Text(arr[0]), new Text(arr[1]));
	}

}

package cn.tedu.friend;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class RelationReducer extends Reducer<Text, Text, Text, IntWritable> {

	public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
		//tom
		//value=rose jim smith lucy
		String name=key.toString();
		List<String> fs=new ArrayList<String>();
		//记录真实好友
		//在MapReducer中,这个迭代器只能被遍历一次
		
		for (Text val : values) {
			String f=val.toString();
			fs.add(f);
			if(name.compareTo(f)<0)
				context.write(new Text(name+"-"+f), new IntWritable(1));
			else
			context.write(new Text(f+"-"+name), new IntWritable(1));
		}
		//根据好友列表来推测关系
		for (int i = 0; i < fs.size()-1; i++) {
			for (int j = i+1; j < fs.size(); j++) {
				String f1=fs.get(i);
				String f2=fs.get(j);
				if(f1.compareTo(f2)<0)
					context.write(new Text(f1+"-"+f2), new IntWritable(0));
				else
					context.write(new Text(f2+"-"+f1), new IntWritable(0));
			}
		}
	}

}

HDFS分布式搭建简图
在这里插入图片描述

HDFS和MapReducer分布式搭建简图
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值