多输入路径-只采一个文件-(MultipleInputs+getsample(conf.getInputFormat)

之前弄采样器,以为已经结束了工作,结果现在又遇到了问题,因为我的输入有两个文件,设计要求是先只采样其中的大文件(未来是两个文件分别采样的),只有一个输入文件且采样时,使用采样器的代码是:

  1. Path input = new Path(args[0].toString());
  2. input = input.makeQualified(input.getFileSystem(conf));
  3. InputSampler.IntervalSampler<Text, NullWritable> sampler = new InputSampler.IntervalSampler<Text, NullWritable>(0.4, 5);
  1. // 这句话的意思是两个分区,
  1. // K[] getSample(InputFormat<K,V> inf, JobConf job)  函数原型
  2. String skewuri_out = args[2] + "/sample_list"; // 存放采样的结果,不是分区的结果
  3. FileSystem fs = FileSystem.get(URI.create(skewuri_out), conf);
  4. FSDataOutputStream fs_out = fs.create(new Path(skewuri_out));
  5. final InputFormat inf = conf.getInputFormat();//这个是获得Jobconf的InputFormat
  6. Object[] p = sampler.getSample(inf, conf);// 输出采样的结果,必须前面是Object类型,换成I那头Writable就不管用了,不知道为什么

但是这样问题就来了,如果我写了两个Mapper类,分别为Map1class,Map2class,现在两个class分别处理两个不同输入路径的数据,目前是指定输入数据的格式是相同的,那么可以用MultipleInputs 来实现:

  1. MultipleInputs.addInputPath(conf, new Path(args[0]), Definemyself.class,Map1class.class);
  2. MultipleInputs.addInputPath(conf, new Path(args[1]), Definemyself.class,Map2class.class);

//Definemyself.class 是我自定义的继承了FileInputFormat ,并且实现了WritableComparable接口

//继承FileInputFormat 是采样的需要,实现WritableComparable接口,是因为我在join的时候想整体数据进行序列化,我自己也解释不明白这个序列化,可以理解成C里面的结构体吧,就是作为一个整体,可以toString()输出。

原型是:public class Definemyself extends FileInputFormat<Text,Text> implements WritableComparable{...}

这个问题从昨晚就困扰我,上周做梦采样,这种做梦还是采样。中午和老公出去吃的,因为要好好探讨一下这个问题,我的理论就是既然系统提供
MultipleInputs,同时Jobconf有能调用getInputFormat(),就肯定有办法二者同时使用,不让就矛盾了,傻子才会建立这
样的系统呢。

终于,吃完饭回来,我又尝试了一下想法,终于让我给解决了,即如何有两个输入路径的情况下,只对一个输入路径中的文件进行采样,啊哈,答案很简单!见下:

首先修改 Definemyself.class,使其在输出的时候能够分辨是哪个表的,这里我使用最简单的方法,两个表的长度不一样,我通过,长
的我称为long表,短的我成为short表,主要就是修改这个class里面的next函数。以下是Definemyself.class中的最重要的
函数。

  1. class EmployeeRecordReader implements RecordReader<Text, Text> {
  2. private LineRecordReader in;
  3. private LongWritable junk = new LongWritable();
  4. private Text line = new Text();
  5. private int KEY_LENGTH = 10;
  6. public boolean next(Text key, Text value) throws IOException {
  7. if (in.next(junk, line)) {
  8. String[] lines = line.toString().split(",");
  9. if (lines.length ==9) { // 这个是大表,也就是要采样的表
  10. // System.out.println("The line in next of Employee is "+ line);
  11. deptno = lines[7];
  12. //
    很多写全排序的,不论是对整数,还是对字符串,都采用的下面这种写法,自己查去,不过对于表的一行有很多列,只取其中的一列作为排序的key时
    value.set要做更改。就是输出正行,别的博客中都是些的value = new Text(), 太局限了,只适合字符串全局排序时使用。其实下
    面的if... else 条件可以省略,留下只是提醒自己,如果从表中抽取的key值占的字节太长时,要做一些处理。
  13. if (deptno.length() < KEY_LENGTH) {
  14. key.set(new Text(deptno));
  15. value.set(new Text(line+","+"0"));// 题阿
  16. } else {
  17. key.set(new Text(deptno));
  18. value.set(new Text(line+","+"0"));
  19. }
  20. return true;
  21. }
  22. else if (lines.length !=9) {// 断表的处理
  23. System.out.println("(for short.txt)The line in next of Employee is "+ line);
  24. deptno = lines[0];// 每列较长的文件中,貌似第5列是depto吧,先这么指定吧
  25. if (deptno.length() < KEY_LENGTH /* &&(!lines[8].equals("0")) */) {
  26. key.set(new Text(deptno));
  27. value.set(new Text(line+","+"1"));
  28. } else{
  29. key.set(new Text(deptno));
  30. value.set(new Text(line+","+"1"));// 1为
  31. }
  32. return true;
  33. }
  34. else return false;
  35. } else {
  36. return false;
  37. }
  38. }
  39. }

以上的更改就是两个表进来,都可通过此类进行输入,无须针对两个表,要写两个继承FileInputFormat并实现
WritableComparable接口的类。下面才是如何让才采样器只采一个文件的,啊哈!答案说出来笑死人了,那就是利用
MultipleInputs先指定要采样的那个输入路径,然后调用采样器,采样结束后于采样相关的流、文件什么的进行关闭,最后再用
MultipleInputs指定第二个输入路径。这样路径一的文件(可以包含多个文本,你懂的)先采样,然后路径一和路径二的文件都进入map
了,map再根据一些额外的信息判断来自那个路径的数据。

MultipleInputs.addInputPath(conf, new Path(args[0]), Definemyself.class,Mapclass.class);//第一个输入路径
        /*********下面采样**********更多采样的细节见我领一篇博客,不一样的视角那篇***********/

        Path input = new Path(args[0].toString());
        input = input.makeQualified(input.getFileSystem(conf));

        InputSampler.RandomSampler<Text, NullWritable> sampler = new InputSampler.RandomSampler<Text, NullWritable>(0.4,20, 5);

        /...........此处省略细节................/

        IOUtils.closeStream(fs_out);// 关闭流,有关采样的结束了。
         /...............此处添加一些其他的需要的工作,例如分布式缓存啦,Hashtable的处理阿............../

         MultipleInputs.addInputPath(conf, new Path(args[3]), Definemyself.class, Mapclass.class); //最后指定输入的第二条路径

         JobClient.runJob(conf);

今天上午最郁闷的就是,碰到很多问题,根本就搜不到,我最讨厌转别人的博客了,如果你觉得别人的博客有用,你自己保存到草稿箱里就好了阿,一搜索就找到一堆相同的东西,无聊,浪费别人时间,让自己积分高了又怎样阿,我保证以后自己的博客,发表出来的一定要是原创。

最近几天遇到的问题刚开始的时候都会让我头大,因为搜就搜不到答案,Google外国的网页又打不开,你懂的。求人不如求自己,这不最后都解决了 吗?或许对于高手来讲这都不是问题,不要嘲笑我阿,《编程珠玑》上都说了,程序出错的地方不是难的地方,难的地方因为我们会事先考虑好,并设计好,难的地 方是一些容易忽略的地方。渐渐我爱上Hadoop了。


由于最近在研究Hadoop中采样的问题,搞的头很大,今天慢慢有些头绪了。先记录点采样器的问题吧。

Hadoop已经内置的若干个采
样器, InputSampler
类实现了Sampler接口,该接口的唯一成员方法是getsampler,返回一系列样本键。这个接口通常不直接由客户端调用,二十由
InputSampler类的静态方法writePartitionFile()调用,目的是创建一个顺序文件(SequenceFile)来存储定义分
区的键。
简单的代码(权威指南中的):

  1. <span style="font-weight: normal;">InputSampler.RandomSampler<IntWritable, NullWritable> sampler = new InputSampler.RandomSampler<IntWritable, NullWritable>(1.0, 20, 3);
  2. Path input = FileInputFormat.getInputPaths(conf)[0];
  3. input = input.makeQualified(input.getFileSystem(conf));
  4. Path partitionFile = new Path(input, "_partitions");
  5. TotalOrderPartitioner.setPartitionFile(conf, partitionFile);
  6. InputSampler.writePartitionFile(conf, sampler);// 目的是创建一个顺序文件来存储定义的分区的键, 这个顺序文件就是路径partitionFile 所指示的文件。</span>

其中 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 的例子。

  1. SequenceFile.Reader read = null;
  2. FileSystem fs = FileSystem.get(partitionURI, conf);
  3. read = new SequenceFile.Reader(fs, partitionFile, conf);
  4. IntWritable key = (IntWritable) ReflectionUtils.newInstance(read.getKeyClass(), conf); //顺序文件中的key类型
  5. NullWritable value = (NullWritable) ReflectionUtils.newInstance(read.getValueClass(), conf);//value类型
  6. while(read.next(key, value)){
  7. // do something you want
  8. // System.out.println(" The key is "+ key.toString());}
  9. IOUtils.closeStream(read); //最后要关闭read

这里
要提醒一下,如果你指定reduce task的个数如果为1,那么即使你采样了,采了很多个,但是writePartitionFile
函数是不会向你指定的顺序文件中写入数据的(想想也是吗?可惜当时脑子坏掉了,就只想着怎么文件是空的,忘记改reduce task的个数了,囧阿)。

这样以来通过读取
partitionFile 所指定的SequenceFile 文件中的数据,就可以获得用于分区的
键值。可以我的需求是要记录每个采样值,并且统计每个采样值出现的次数。找了很久的实现方法,关键是我自己又不想自己重新写一个类来继承
InputSampler。找了很久,根本搜不到(搜到的全是用采样器来帮助TotalOrderParition的,或者Tera排序的)

函数原型如下:

K[]

 

getSample(InputFormat<K,V> inf, JobConf job) 
          Randomize the split order, then take the specified number of
keys from each split sampled, where each key is selected with the
specified probability and possibly replaced by a subsequently selected
key when the quota of keys from that split is satisfied.

 

顾名思义,k[]是用来获得采样的结果序列的。可是实现时又遇到问题。按照我自己设定的输入输出格式,写了如下代码:

IntWrtiable[] k;

InputFormat<IntWritable, NullWritable> inf;

k = sampler.getSample(inf,conf);

之后就是一堆错误,什么key的类型不匹配阿,还有这样定义inf就是错误的,可怜我java不熟悉,有时候很简单的东西我还是弄错,没办法,慢慢进步吧。

后来发现不能定义inf,而是将inf参数写成 conf.getInputFormat() 这样参数部分就没有什么类型不匹配的问题了,又遇到了新的问题,即使我输入是key是IntWritable类型的,可以写成

IntWritable[]
k = sampler.getsample(conf.getInputFormat(),conf)又报错,是呢么key是object
不能强制转化成IntWritable。OMG,发现API上将的根本就不详细阿。而且gersample的使用,我搜索都没搜到,我恨不得去
StackOverFlow上提问去了。

把老公叫过来调程序,我俩折腾了半天,发现要这样写:

Object[] p = sampler.getSample(conf.getInputFormat(), conf);

这样就OK了,如果要把这
些采样结果写进单独的文件中,就使用 FileSystem 和FSDataOutputStream就可以了,要统计结果,定义一个私有静态
hash表就可以了。采样器是我本解段工作的最后一个模块了,下一步就是整合之前的工作了,6月底的时候希望程序和测试实验都结束。

最近很累,又要做项目,又要准备找工作,做个IT女真不容易阿。

谨以此篇文章献给我最爱的老公,么么。(你都不用找工作,跑去香港读博,哼)

hadoop2.2原理:采样器的更多相关文章

  1. hadoop2.2原理: 序列化浅析

    序列化是指将一个对象编码成字节流,之后从字节流中重构对象: 为什么需要序列化? 答:用序列化接口可以将对象实例从存储到本地文件或者传送到网络的另一端的节点上: 序列化过程: 序列化的三种主要用途: 1 ...

  2. hadoop2.2原理:分析HDFS的文件读写

    File Read 程序举例: public class FileRead { public static void main(Sting[] args) throws Exception { Con ...

  3. hadoop编程技巧(4)---总体情况key按类别搜索TotalOrderPartitioner

    Hadoop代码测试版:Hadoop2.4 原理:携带MR该程序随机抽样提取前的输入数据,样本分类,然后,MR该过程的中间Partition此值用于当样品排序分组数据.这使得可以实现全球排名的目的. ...

  4. 大数据学习之路(1)Hadoop生态体系结构

    Hadoop的核心是HDFS和MapReduce,hadoop2.0还包括YARN. Hadoop1.x的生态系统: Hadoop2.x引入YARN: HDFS(Hadoop分布式文件系统)源自于Go ...

  5. Hadoop2.7.6_03_HDFS原理

    1. HDFS前言 l  设计思想 分而治之:将大文件.大批量文件,分布式存放在大量服务器上,以便于采取分而治之的方式对海量数据进行运算分析: l  在大数据系统中作用: 为各类分布式运算框架(如:m ...

  6. [置顶] Hadoop2.2.0中HDFS的高可用性实现原理

    在Hadoop2.0.0之前,NameNode(NN)在HDFS集群中存在单点故障(single point of failure),每一个集群中存在一个NameNode,如果NN所在的机器出现了故障 ...

  7. Hadoop2.6.0的FileInputFormat的任务切分原理分析(即如何控制FileInputFormat的map任务数量)

    前言 首先确保已经搭建好Hadoop集群环境,可以参考<Linux下Hadoop集群环境的搭建>一文的内容.我在测试mapreduce任务时,发现相比于使用Job.setNumReduce ...

  8. hadoop2—namenode—HA原理详解

    在hadoop1中NameNode存在一个单点故障问题,也就是说如果NameNode所在的机器发生故障,那么整个集群就将不可用(hadoop1中有个SecorndaryNameNode,但是它并不是N ...

  9. 【原创 Hadoop&Spark 动手实践 4】Hadoop2.7.3 YARN原理与动手实践

    简介 Apache Hadoop 2.0 包含 YARN,它将资源管理和处理组件分开.基于 YARN 的架构不受 MapReduce 约束.本文将介绍 YARN,以及它相对于 Hadoop 中以前的分 ...

随机推荐

  1. Cannot connect to (local) sql server 2008

    Make following steps to solve the issue: Cannot connect to (local). ADDITIONAL INFORMATION: Login fa ...

  2. JavaScript 中的内存泄漏

    JavaScript 中的内存泄漏 JavaScript 是一种垃圾收集式语言,这就是说,内存是根据对象的创建分配给该对象的,并会在没有对该对象的引用时由浏览器收回.JavaScript 的垃圾收集机 ...

  3. 查找计算机IP及占用端口

    1. 在电脑启动搜索框,输入cmd回车打开命令提示符窗口. 输入ipconfig,就可以查看电脑的子网淹没,默认网关,IP等信息. 2. 查看本机开放的端口,即已被占用的端口号. 命令: netsta ...

  4. C#多线程(一)

    一.定义与理解 1.定义 线程是操作系统分配CPU时间片的基本单位,每个运行的引用程序为一个进程,这个进程可以包含一个或多个线程. 线程是进程中的执行流程,每个线程可以得到一小段程序的执行时间,在单核 ...

  5. LED字符设备驱动实例及测试代码

    驱动代码如下: #include <linux/kernel.h>//内核头文件 #include <linux/init.h>//__init等 #include <l ...

  6. hive与hbase的区别与联系

    共同点:1.hbase与hive都是架构在hadoop之上的.都是用hadoop作为底层存储 区别:2.Hive是建立在Hadoop之上为了减少MapReduce jobs编写工作的批处理系统,HBa ...

  7. CODEVS 1004四子连棋

    [题目描述 Description] 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑 ...

  8. node.js 1Million concurrent connections!

    https://github.com/ashtuchkin/node-millenium http://blog.caustik.com/2012/08/19/node-js-w1m-concurre ...

  9. linux下定时发送邮件

    at命令可以在某个时间运行某个程序,而mail可以以命令行的方式把存于一个文本中的邮件正文发送抄送出去. 具体用法:  1. 把email正文准备好,比如写在email.txt里  2. 然后写一个脚 ...

  10. android apk 反编译

    Apk文件结构 apk文件实际是一个zip压缩包,可以通过解压缩工具解开.以下是我们用zip解开helloworld.apk文件后看到的内容.可以看到其结构跟新建立的工程结构有些类似. java代码: ...