由于最近在研究Hadoop中采样的问题,搞的头很大,今天慢慢有些头绪了。先记录点采样器的问题吧。
Hadoop已经内置的若干个采样器, InputSampler 类实现了Sampler接口,该接口的唯一成员方法是getsampler,返回一系列样本键。这个接口通常不直接由客户端调用,二十由InputSampler类的静态方法writePartitionFile()调用,目的是创建一个顺序文件(SequenceFile)来存储定义分区的键。
简单的代码(权威指南中的):
InputSampler.RandomSampler<IntWritable, NullWritable> sampler = new InputSampler.RandomSampler<IntWritable, NullWritable>(1.0, 20, 3);
Path input = FileInputFormat.getInputPaths(conf)[0];
input = input.makeQualified(input.getFileSystem(conf));
Path partitionFile = new Path(input, "_partitions");
TotalOrderPartitioner.setPartitionFile(conf, partitionFile);
InputSampler.writePartitionFile(conf, sampler);// 目的是创建一个顺序文件来存储定义的分区的键, 这个顺序文件就是路径partitionFile 所指示的文件。
其中 writePartitionFile
(JobConf job, InputSampler.Sampler<K,V> sampler)这个函数是将采样的结果排序,然后按照分区的个数n,将排序后的结果平均分为n分,取n-1个分割点,这个分割点具体取的时候,运用了一些4舍5入的方法,最简答的理解就是取后n-1个组中每组的第一个采样值就OK了。
上面提到了SequenceFile. 那这里就简单插一下SequenceFile文件的读问题吧。 目的是用于记录二进制类型的key/value对的,SequenceFile 同样也可作为小文件的容器。提供了Writer,Reader 和 SequenceFile.Sorter 三个类用于完成写,读,和排序。
以下是读一个SequenceFIle 的例子。
SequenceFile.Reader read = null;
FileSystem fs = FileSystem.get(partitionURI, conf);
read = new SequenceFile.Reader(fs, partitionFile, conf);
IntWritable key = (IntWritable) ReflectionUtils.newInstance(read.getKeyClass(), conf); //顺序文件中的key类型
NullWritable value = (NullWritable) ReflectionUtils.newInstance(read.getValueClass(), conf);//value类型
while(read.next(key, value)){
// do something you want
// System.out.println(" The key is "+ key.toString());}
IOUtils.closeStream(read); //最后要关闭read
这里要提醒一下,如果你指定reduce task的个数如果为1,那么即使你采样了,采了很多个,但是writePartitionFile 函数是不会向你指定的顺序文件中写入数据的(想想也是吗?可惜当时脑子坏掉了,就只想着怎么文件是空的,忘记改reduce task的个数了,囧阿)。
这样以来通过读取partitionFile 所指定的SequenceFile 文件中的数据,就可以获得用于分区的 键值。可以我的需求是要记录每个采样值,并且统计每个采样值出现的次数。找了很久的实现方法,关键是我自己又不想自己重新写一个类来继承InputSampler。找了很久,根本搜不到(搜到的全是用采样器来帮助TotalOrderParition的,或者Tera排序的)
函数原型如下:
| |
顾名思义,k[]是用来获得采样的结果序列的。可是实现时又遇到问题。按照我自己设定的输入输出格式,写了如下代码: