import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile.CompressionType;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.compress.BZip2Codec;
import org.apache.hadoop.mapred.lib.InputSampler;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.filecache.DistributedCache;
import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.TotalOrderPartitioner;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import com.lenovo.caojian.quanweizhinan.mapreduce.MissingTemperatureFields_new_version;
/**全排序
* 如何用hadoop产生一个全局排序的文件?最简单的方法是使用一个分区(a single partition)
* 但该方法在处理大型文件时效率极低,因为一台机器必须处理所偶的输出文件,从而完全丧失了Mapreduce所提供的并行架构
* (更好的回答是使用pig 或者hive,他们都可以使用一条指令进行排序)
* (select * from (select * from table distribute by time sort by time desc limit 50 ) t order by time desc limit 50;);
* (select * from (select * from table distribute by time sort by time desc limti 50) t order by time desc limit 50);
* 事实上任有替代方案:首先,创建一系列排好序的文件,其次串联这些文件,最后生成一个全局排序的文件,主要思想是使用一个一个partitioner来描述输出
* 的全局排序,例如,可以为上述文件创建四个分区,在第一个分区。。。。。。。。。。。。。
* 该方法的关键是在于如何划分各个分区,理想情况下,各个分区所含记录数应该大致相等,使作业的总体执行时间不会受制于个别reduce。
* 在前面提到的分区方案中。各分区的相对大小如下所示。
* InputSampler类实现了Sampler接口,该接口的唯一成员方法(即getSampler)有两个输入参数(一个InputFormat对象,和一个job对象)
* 返回一系列样本键
* public interface Sampler<K,V>{
* K[] getSample(InputFormat<K,V> inf, Job job)throws IOExceeption, InterruptedException;
* }
*
* 8-8调用TotalOrderPartitioner按IntWritable键对顺序文件进行全局排序
* 该程序使用RandomSampler以指定的采样率均匀的从一个数据集中选择样本,
* 在本例中,采样率设置为0.1 ,RamdomSampler的输入参数还包括最大样本数和最大分区(本例中分别是 10000,10这也是InputSampler作为应用程序运行时默认设置
* 只要任意一个限制条件满足,即停止采样。采样器在客户端运行, 因此限制分片的下载量以加速采样器的运行就尤为重要
* 在实战中,采样器的运行时间仅占用运行时间的小部分。
* 为了和集群上面运行的其他任务共享分区文件, InputSampler需要将其缩写的分区文件加入到分布缓存中
* 以下方案分别以 -5.6C ,13.9C, 22.0C 为边界得到4个分区,易知,新方案比旧方案更加均匀。
* 输入数据的特性决定如何挑选最合适的采样器,以SplitSampler为例,它只采样了一个分片中的前N条记录,由于并
* 未从所有分片中广泛采集,该采样器并不适合已经排好序的数据。
* 另外一方面,IntervalSample以一定的间隔定期从分片中选择键,因此对于已经排好序的数据来说是一个更好的选择
* RandomSampler是优秀的通用采样器,如果没有可以满足应用需要(记住采样器的目的是创建大小近似相等的一系列分区)
* ,智能写程序来实现Sampler接口。
* InputSampler类和TotalOrderPartitioner类的一个好特性是用户可以自由定义分区数,
* 该值通常取决与集群上reducer槽的数量(该值需要稍小于reducer槽的总数,以应付可能出现的故障)
* 由于TotalOrderPartitioner只用于分区边界均不相同的时候,因而当键空间小时,设置太大的分区
* 可能会导致数据冲突。
* hadoop jar hadoop-examples.jar SortByTemperatureUsingTotalOrderPartitioner
* -D mapred.reduce.tasks=30 input/ncdc/all-seq output-totalsort
* 该程序输出30个内部排好序的分区,切分区中i中的所有键小于分区i+1中的键。
*/
/**
* Sampler接口有三个子类
* 1、IntervalSampler 以一定的时间间隔定期从分片中选择键
* 2、RandomSampler 随机选择
* 3、SplitSampler 只采样分片中的前n条记录
*/
public class SortByTemperatureUsingTotalOrderPartitioner extends Configured implements Tool {
@Override
public int run(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf);
if (job == null) {
return -1;
}
job.setInputFormatClass(SequenceFileInputFormat.class);
job.setOutputKeyClass(IntWritable.class);
job.setOutputFormatClass(SequenceFileOutputFormat.class);
SequenceFileOutputFormat.setCompressOutput(job, true);
SequenceFileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);
SequenceFileOutputFormat.setOutputCompressionType(job, CompressionType.BLOCK);
job.setPartitionerClass(TotalOrderPartitioner.class);
InputSampler.Sampler<IntWritable, Text> sampler =
new InputSampler.RandomSampler<IntWritable, Text>(0.1, 1000, 10);
InputSampler.writePartitionFile(job, sampler);
String partitionFile = TotalOrderPartitioner.getPartitionFile(conf);
URI partitionUri = new URI(partitionFile + "#" + TotalOrderPartitioner.DEFAULT_PATH);
DistributedCache.addCacheFile(partitionUri, conf);
DistributedCache.createSymlink(conf);
return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args) throws Exception {
int run = ToolRunner.run(new MissingTemperatureFields_new_version(), args);
System.exit(run);
}
}
hadoop权威指南 page= 297