Hadoop-MapReduce-CombineTextInputFormat,KeyValueTextInputFormat,NLineInputFormat,自定义InputFormat

CombineTextInputFormat

    Hadoop默认的TextInputFormat切片机制是对任务按文件切片,不管文件多小,都会是一个单独的切片,然后交给一个MapTask。如果有大量小文件,就会产生大量MapTask,处理效率非常低。
    CombineTextInputFormat用于处理大量小文件,它可以将多个小文件从逻辑上看成一个切片。多个小文件可以交给一个MapTask处理。
虚拟存储过程
    setMaxInputSplitSize设置最大切片大小,假设4M。
    如果文件大小小于最大切片大小,则单独划分为一块,如一个2.5M的文件划分为一块。
    如果文件大小大于最大切片大小但小于最大切片大小的两倍,则平分为两块(防止出现太小切片),如5M划分两块,各2.5M。
    如果文件大小大于最大切片大小的两倍,则以最大值切一块。
    例如setMaxInputSplitSize值为4M,输入文件大小为8.02M,则先逻辑上分成一个4M,剩余的大小为4.02M。如果按照4M逻辑划分,就会出现0.02M的小的虚拟存储文件,所以将剩余的4.02M文件切分成(2.01M和2.01M)两个文件。
切片过程
    判断虚拟存储的文件大小是否大于setMaxInputSplitSize值,大于等于则单独形成一个切片。
    如果不大于,则跟下一个虚拟存储文件进行合并,形成一个切片。如两个文件,1.5M和3M,形成一个4.5M的切片。

    在驱动类添加如下代码,采用CombineTextInputFormat。

// 如果不设置InputFormat,它默认用的是TextInputFormat.class
job.setInputFormatClass(CombineTextInputFormat.class);

//虚拟存储切片最大值设置4m
CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);

KeyValueTextInputFormat

    每一行被分割符(第一个分隔符)分割为key,value。可以通过在驱动类设置 conf.set(KeyValueLineRecordReader.KEY_VALUE_SEPERATOR, “\t”) 来设定分割符,默认分割符是 tab("\t")。

    在驱动类添加如下代码,采用KeyValueTextInputFormat。

//设定分割符,默认分割符是\t
conf.set(KeyValueLineRecordReader.KEY_VALUE_SEPERATOR, " ");

// 如果不设置InputFormat,它默认用的是TextInputFormat.class
job.setInputFormatClass(KeyValueTextInputFormat.class);

NLineInputFormat

    每个MapTask处理的InputSplit不再按block块进行划分,而是按指定的行数N来划分。即输入文件的总行数 ÷ N = 切片数,如果不整除,切片数=商+1。.

    在驱动类添加如下代码,采用NLineInputFormat。

//设定分割符,默认分割符是\t
NLineInputFormat.setNumLinesPerSplit(job, 3);

// 如果不设置InputFormat,它默认用的是TextInputFormat.class
job.setInputFormatClass(NLineInputFormat.class);

自定义InputFormat

    自定义InputFormat,将多个小文件合并成一个SequenceFile文件(SequenceFile文件是Hadoop用来存储二进制形式的key-value对的文件格式),SequenceFile里面存储着多个文件,key为文件路径+名称为,value为文件内容。
    自定义一个类继承FileInputFormat,重写createRecordReader(),创建自定义的RecordReader对象,并初始化initialize。

import java.io.IOException;
import org.apache.hadoop.io.BytesWritable;
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;

public class DIYInputFormat extends FileInputFormat<Text, BytesWritable>{
    
    
	@Override
	public RecordReader<Text, BytesWritable> createRecordReader(InputSplit split, TaskAttemptContext context)
			throws IOException, InterruptedException {
    
    
		DIYRecordReader reader = new DIYRecordReader();
		reader.initialize(split, context);
		return reader;
	}
}

    键是文件的全路径,值是文件内容。nextKeyValue()在Mapper.run()方法调用,每个切片会新创建一个DIYRecordReader对象。

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.IOUtils;
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.FileSplit;

public class DIYRecordReader extends RecordReader<Text, BytesWritable>{
    
    

	FileSplit split;
	Configuration conf;
	Text key = new Text();
	BytesWritable value = new BytesWritable();
	boolean isProgress = true;
	
	@Override
	public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
    
    
		this.split = (FileSplit) split;
		this.conf = context.getConfiguration();
	}

	@Override
	public boolean nextKeyValue() throws IOException, InterruptedException {
    
    
		if (isProgress) {
    
    
			//1.获取fs对象
			Path path = split.getPath();
			FileSystem fs = path.getFileSystem(conf);
			//2.获取输入流
			FSDataInputStream fis = fs.open(path);
			//3.拷贝
			byte[] buf = new byte[(int) split.getLength()];
			IOUtils.readFully(fis, buf, 0, buf.length);
			//4.封装key
			key.set(path.toString());
			//5.封装value
			value.set(buf, 0, buf.length);
			//6.关闭资源
			IOUtils.closeStream(fis);
			isProgress = false;
			return true;
		}
		return false;
	}

	@Override
	public Text getCurrentKey() throws IOException, InterruptedException {
    
    
		return key;
	}

	@Override
	public BytesWritable getCurrentValue() throws IOException, InterruptedException {
    
    
		return value;
	}

	@Override
	public float getProgress() throws IOException, InterruptedException {
    
    
		return 0;
	}

	@Override
	public void close() throws IOException {
    
    }
}
import java.io.IOException;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class DIYInputFormatMapper extends Mapper<Text, BytesWritable, Text, BytesWritable> {
    
    
	@Override
	protected void map(Text key, BytesWritable value, Mapper<Text, BytesWritable, Text, BytesWritable>.Context context)
			throws IOException, InterruptedException {
    
    
		context.write(key, value);
	}
}
import java.io.IOException;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class DIYInputFormatReducer extends Reducer<Text, BytesWritable, Text, BytesWritable> {
    
    
	@Override
	protected void reduce(Text key, Iterable<BytesWritable> values,
			Reducer<Text, BytesWritable, Text, BytesWritable>.Context context) throws IOException, InterruptedException {
    
    
		for (BytesWritable value : values) {
    
    
			context.write(key, value);
		}
	}
}

    驱动类设置InputFormat为自定义的InputFormat。设置OutputFormat为SequenceFileOutputFormat,多个文件输出成一个文件,键为全路径,值为文件内容,访问时输入全路径即可获得内容。

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
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 org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;

public class DIYInputFormatDriver {
    
    
	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
    
    
		args = new String[] {
    
     "e:/input", "e:/output"};
		Configuration conf = new Configuration();
		Job job = Job.getInstance(conf);
		job.setJarByClass(DIYInputFormatDriver.class);
		job.setMapperClass(DIYInputFormatMapper.class);
		job.setReducerClass(DIYInputFormatReducer.class);
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(BytesWritable.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(BytesWritable.class);
		FileInputFormat.setInputPaths(job, new Path(args[0]));
		FileOutputFormat.setOutputPath(job, new Path(args[1]));
		//设置输入的InputFormat
		job.setInputFormatClass(DIYInputFormat.class);
		//设置输出的OutputFormat
		job.setOutputFormatClass(SequenceFileOutputFormat.class);
		boolean result = job.waitForCompletion(true);
		System.exit(result ? 0 : 1);
	}
}

InputFormat总结

  <K, V> 切片
TextInputFormat(默认) 键是存储该行在整个文件中的起始字节偏移量,LongWritable类型。
值是该行的内容,不包括任何行终止符(换行符和回车键),Text类型。
按块的大小切片,本地是32M;集群Hadoop1.0是64M;2.0是128M
CombineTextInputFormat 键和值与TextInputFormat一样 按给定大小切片
KeyValueTextInputFormat 键是该行第一个分隔符左边的内容。
值是该行第一个分隔符右边的内容。
按块的大小切片
NLineInputFormat 键和值与TextInputFormat一样 按指定数量的行切块片*自定义InputFormat**

    

猜你喜欢

转载自blog.csdn.net/H_X_P_/article/details/106006554