叫你写mapreduce--lesson2

来源:互联网 发布:刷火车票的软件 编辑:程序博客网 时间:2024/04/29 18:00

MapReduce第二节

 

还记得上一节的一个mr例子吗,对文件进行排序

#bin/hadoop jarcontrib/streaming/hadoop-streaming-1.2.1.jar -D mapred.compress.map.output=truemapred.job.reuse.jvm.num.tasks=4  -input/wolf1 -output /wolf2 -mapper /bin/cat -reducer /bin/cat

这里我们指定了几个参数–input  这里是指定文件输入目录

                                                  -output 这里是指定reduce结果输出目录

一、用java写mapreduce

1.1、自定义map函数

         Mapreduce框架提供了map接口,实现这个接口,就自定义了map函数

publicclass Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {

publicclass Context

extends MapContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {

public Context(Configuration conf, TaskAttemptID taskid,

                   RecordReader<KEYIN,VALUEIN> reader,

                   RecordWriter<KEYOUT,VALUEOUT> writer,

                   OutputCommitter committer,

                   StatusReporter reporter,

                   InputSplit split) throws IOException, InterruptedException {

super(conf, taskid, reader, writer, committer, reporter, split);

    }

  }

/**

   * 初始化方法,仅被调用一次,在map函数运行之前

   */

protectedvoid setup(Context context);

/**

   * map函数

   */

protectedvoid map(KEYIN key, VALUEIN value, Context context){

context.write((KEYOUT) key, (VALUEOUT) value);

}

 

/**

   *清理函数,map任务运行结束后,仅被调用一次

   */

protectedvoid cleanup(Context context)

 

/**

   * 运行函数,map任务的入口

   */

publicvoid run(Context context) {

    setup(context);

try {

while (context.nextKeyValue()) {

        map(context.getCurrentKey(), context.getCurrentValue(), context);

      }

    } finally {

      cleanup(context);

    }

  }

}

1.2、自定义reduce函数

Mapreduce框架提供了reduce接口,实现这个接口,就自定义了reduce函数

publicclass Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {

 

publicclass Context

extends ReduceContext<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {

public Context(Configuration conf, TaskAttemptID taskid,

                   RawKeyValueIterator input,

Counter inputKeyCounter,

Counter inputValueCounter,

                   RecordWriter<KEYOUT,VALUEOUT> output,

                   OutputCommitter committer,

                   StatusReporter reporter,

                   RawComparator<KEYIN> comparator,

                   Class<KEYIN> keyClass,

                   Class<VALUEIN> valueClass

                   ) throws IOException, InterruptedException {

super(conf, taskid, input, inputKeyCounter, inputValueCounter,

            output, committer, reporter,

            comparator, keyClass, valueClass);

    }

  }

 

/**

*初始化方法,仅被调用一次,在map函数运行之前

*/

protectedvoid setup(Context context);

 

  /**

* reduce函数

*/

protectedvoid reduce(KEYIN key, Iterable<VALUEIN> values, Context context){

for(VALUEIN value: values) {

context.write((KEYOUT) key, (VALUEOUT) value);

}

  }

 

/**

* 清理函数,reduce任务运行结束后,仅被调用一次

*/

protectedvoid cleanup(Context context;

 

  /**

* reduce任务入口

*/

publicvoid run(Context context)throws IOException, InterruptedException {

    setup(context);

try {

while (context.nextKey()) {

        reduce(context.getCurrentKey(), context.getValues(), context);

      }

    } finally {

      cleanup(context);

    }

  }

}

 

1.3、利用InputFormat定义map输入

         Map的输入数据从哪儿来呢?答案是可以从任何地方,hdfs文件、oracle、hbase等等。为map变出数据的魔术师正是----InputFormat。

         只要实现InputFormat接口,就可以为Map指定输入。

        

publicabstractclassInputFormat<K, V> {

 

/**

   * 将输入数据切分成多个InputSplit

   */

publicabstract

    List<InputSplit> getSplits(JobContext context);

/**

   * InputSplit创建读取类:RecordReader

   */

publicabstract

    RecordReader<K,V> createRecordReader(InputSplit split,

                                         TaskAttemptContext context

                                        );

}

1.4、利用outPutFormat定向reduce输出

         那么redcue输出结果,放到哪儿呢?可以是任何地方,hdfs、oralcle、hbase等等。

         只要实现outPutFormat接口,就可以指定输出。

publicabstractclass OutputFormat<K, V> {

 

/**

   * 获取RecordWriter

   * @param context存储当前任务相关信息.

   */

publicabstract RecordWriter<K, V>

    getRecordWriter(TaskAttemptContext context

                    ) throws IOException, InterruptedException;

 

/**

   *

   */

publicabstractvoid checkOutputSpecs(JobContext context

                                        ) throws IOException,

                                                 InterruptedException;

 

/**

   *

   */

publicabstract

  OutputCommitter getOutputCommitter(TaskAttemptContext context

                                     ) throws IOException, InterruptedException;

}

 

 

1.5、现成的InputFormat、outputFormat

         编写InputFormat、outputform并不轻松,考虑到多种数据源,工作量可想而知,好在hadoop为我们提供了多种实现。

1.5.1    InputFormat

名称

数据来源

Key

Value

TextInputFormat

hdfs文件

LongWritable:行号

Text:文件里面一行内容

OracleDataDrivenDBInputFormat

Oracle表

LongWritable:行号

Textends DBWritable(resultset)

MultiTableInputFormat

多张hbase表

ImmutableBytesWritable:行键

org.apache.hadoop.hbase.client.Result

:Result

 

1.5.2    OutputFormat

名称

数据输出

Key

Value

TextOutputFormat

hdfs文件

Text

或者实现toString方法

Text

或者实现toString方法

DBOutputFormat

Oracle表

Textends DBWritable

无意义

MultiTableInputFormat

多张hbase表

ImmutableBytesWritable:表名

Put:向hbase写操作

Delete:向hbase删除操作

 

 

1.6 实现一个简单的MR

1.6.1 功能

                   从base_netizen_register表提取属性关联关系,对相同网民的属性建立关联关系,输出到hdfs文件中。

       陈永鑫 11003   15804250106 11031

 

表名

base_netizen_register

字段名

类型

中文注释

数据1

数据2

数据3

NETIZEN_ID

VARCHAR2(255)

网民id

chenyongxin0321

chenyongxin0321

chenyongxin0321

WEBSITE

VARCHAR2(255)

网站域名

my.51job.com

my.51job.com

my.51job.com

ATTRIBUTE_TYPE

INTEGER

属性类型

11003

11031

11097

ATTRIBUTE_VALUE

VARCHAR2(4000)

属性值

陈永鑫

15804250106

210111198703212537

1.6.2实现

       1.6.2.1  map函数

package test;

 

import java.io.IOException;

import java.sql.SQLException;

 

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.Text;

 

publicclass testMapextends  org.apache.hadoop.mapreduce.Mapper<LongWritable, DbRow,Text,Text>{

    publicvoid map(LongWritable key, DbRow value,

             Context context) throws IOException, InterruptedException{

       try {

           Text oKey = new Text(value.values.getString("netizen_id") +"_"

                            + value.values.getString("website") );

           String oValue = value.values.getString("attribute_type")

                         + "\t"

                         +  value.values.getString("attribute_value");

          

           context.write(oKey, new Text(oValue));

          

       } catch (SQLException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

           return;

       }

    }

   

}

 

       1.6.2.2  reduce函数

package test;

 

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

 

import org.apache.hadoop.io.Text;

 

publicclass testReduceextends

org.apache.hadoop.mapreduce.Reducer<Text ,Text, Text, Text> {

    publicvoid reduce(Text key, Iterable<Text> values,

             Context context) throws IOException, InterruptedException{

       List<String> attrs = new ArrayList<String>();

       for(Text t1:values)

           attrs.add(t1.toString());

       for(String t1:attrs){

           String[] attr1 = t1.split("\t",10);

           Text outkey = new Text(attr1[1] +"_" + attr1[0]);

           for(Stringt2:attrs){

              if(t1.equals(t2)){

                  continue;

              }

              String[] attr2 = t2.split("\t",10);

              context.write(outkey, new Text(attr2[1] +"_" + attr2[0]));

           }

       }

    }

}

       1.6.2.3  main函数

package test;

 

import java.io.IOException;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.filecache.DistributedCache;

import org.apache.hadoop.fs.FileSystem;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.lib.db.DBConfiguration;

import org.apache.hadoop.mapreduce.lib.db.DBWritable;

import org.apache.hadoop.mapreduce.lib.db.OracleDataDrivenDBInputFormat;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

 

publicclass Main {

    static Log_log = LogFactory.getLog(Main.class);

   

    publicstaticvoid main(String[] args)throws IOException, InterruptedException, ClassNotFoundException{

       Configuration conf = new Configuration();

       Job job = new Job(conf,"NeEt" );

       /*

        * 设置map

        */

       job.setInputFormatClass(OracleDataDrivenDBInputFormat.class);

       job.setMapOutputKeyClass(Text.class);

       job.setMapOutputValueClass(Text.class);

       job.setMapperClass(testMap.class);

      

       /*

        * 设置reduce

        */

       job.setOutputFormatClass(TextOutputFormat.class);

       FileOutputFormat.setOutputPath(job, new Path("/wolf1/"));

       job.setOutputKeyClass(Text.class);

       job.setOutputValueClass(Text.class);

       job.setReducerClass(testReduce.class);

       job.setNumReduceTasks(1);

      

       /*

        * 设置oracle相关配置

        */

       job.getConfiguration().set(DBConfiguration.URL_PROPERTY,"jdbc:oracle:thin:@15.15.17.68:1521:ora11g");

       job.getConfiguration().set(DBConfiguration.USERNAME_PROPERTY,"noahdbn");

       job.getConfiguration().set(DBConfiguration.PASSWORD_PROPERTY,"noahdbn");

        job.getConfiguration().set(DBConfiguration.DRIVER_CLASS_PROPERTY,"oracle.jdbc.driver.OracleDriver" );

       job.getConfiguration().set(DBConfiguration.INPUT_TABLE_NAME_PROPERTY," base_netizen_register ");

       job.getConfiguration().set(DBConfiguration.INPUT_FIELD_NAMES_PROPERTY," netizen_id,website,attribute_type,attribute_value,capturetime,datasource ");

       String cond = " 1 = 1 and rownum < 100  ";

   

       job.getConfiguration().set(DBConfiguration.INPUT_CONDITIONS_PROPERTY, cond);

       job.getConfiguration().setClass(DBConfiguration.INPUT_CLASS_PROPERTY, DbRow.class, DBWritable.class);

       _log.info("sql:" +"select " + conf.get(DBConfiguration.INPUT_FIELD_NAMES_PROPERTY) +" from "

              + conf.get(DBConfiguration.INPUT_TABLE_NAME_PROPERTY) +" where " +  cond);

      

      

       /*

        *

        */

       job.setJarByClass(test.Main.class);

       DistributedCache.addArchiveToClassPath(new Path("/wfp/ojdbc5.jar")

       , job.getConfiguration()

       , FileSystem.get(job.getConfiguration()));

      

       /*

        * 运行

        */

       job.waitForCompletion(true);

      

    }

}

 

二、MR调试

2.1      常见错误

        

错误名称

发送时间点

错误原因

解决办法

Exception in thread "main" org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory /wolf1 already exists

任务启动前(准备期间)

输出目录已经存在,

 

删除输出目录

java.lang.RuntimeException: java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver

任务启动前(准备期间)

我们自己的类库,没有加入的hadoop安装目录的lib下面

 

将我们自己调用的jar包拷贝到hadoop lib目录下

java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver

任务启动后

 

找不到我们依赖jar包的类

DistributedCache.addArchiveToClassPath(new Path("/wfp/ojdbc5.jar")

       , job.getConfiguration()

       , FileSystem.get(job.getConfiguration()));

 

 

2.2      调试技巧

2.2.1 通过jobtracker页面查看mr运行情况

         http://15.15.36.104:50030/jobtracker.jsp

2.2.1 如何打印调试信息

         由于mr程序是分布式运行,查看调试信息比较困难,这里介绍一种方法,

在map、reduce函数,调用Systerm.out打印输出日志。然后可以通过页面查看调试信息

        

 

2.3      搭建elipse MR开发环境

1、拷贝Hadoop项目中的eclipse plugin jar文件到eclipse 安装目录plugin目录下

2、重启eclipse,配置hadoop installation directory

如果安装插件成功,打开Window-->Preferens,你会发现Hadoop Map/Reduce选项,在这个选项里你需要配置Hadoop installation directory。配置完成后退出。 

3.配置Map/Reduce Locations 

Window-->Show View->other...,在MapReduce Tools中选择Map/ReduceLocations

 


Map/Reduce LocationsEclipse界面的正下方)中新建一个Hadoop Location。在这个View中,点击鼠标右键-->New HadoopLocation。在弹出的对话框中你需要配置Location name,可任意填,如Hadoop,以及Map/ReduceMasterDFS Master。这里面的HostPort分别为你在mapred-site.xmlcore-site.xml中配置的地址及端口。我的这两个文件中配置如下: 

mapred-site.xml

[html] view plaincopy

  1. <property>  
  2. <name>mapred.job.tracker</name>  
  3. <value>matraxa:9001</value>  
  4. </property>  

core-site.xml:

[html] view plaincopy

  1. <property>  
  2. <name>fs.default.name</name>  
  3. <value>hdfs://matraxa:9000</value>  
  4. </property>  

 

最后的配置截图如下:

 

设置完成后,点击Finish就应用了该设置。然后,在最左边的Project Explorer中就能看到DFS的目录,如下图所示:


4、建立、运行hadoop程序

1、新建项目。 
File-->New-->Other-->Map/Reduce Project 
项目名可以随便取,如hadoopTest  

三  MR优化技巧

3.1 合理设置reduce、map数量

         默认情况下,reduce数量为1,所以当reduce运行比较慢的时候,可以多设置reduce任务数量,提高性能

        

属性名称

类型

默认值

说明

mapred.reduce.tasks

Int

1

Reduce任务数量

mapred.map.tasks

Int

动态决定

可以人为设置

 

3.2 jvm重用

         默认情况下,hadoop,每启动一个map或reduce任务,就启动一个新的jvm。

         Jvm重用的好处?1、节省启动jvm时间  2、可以充分利用HotSpot JVM所用的运行时优化

        

属性名称

类型

默认值

说明

mapred.job.reuse.jvm.num.tasks

Int

1

在一个tasktracker上面给定作业的每个jvm可以运行的任务最大数

 

3.3推测式执行开关

         什么是推测式执行?一个mr任务启动会,会检测map、reduce任务执行情况,如果一个map或reduce任务,运行得异常缓慢,则会开启一个相同的map或reduce任务,作为备份。

         推测执行的好处?一个map任务可能由于网络、硬盘老化等问题,执行很慢,拖这个job后腿,推测式执行,开启一个备份,可以避免一个点的异常状况。

坏处?开启备份,会造成资源浪费,有些时候,任务执行慢,无法避免,开启备份还是慢,并且占用宝贵的资源

属性名称

类型

默认值

说明

mapred.map.tasks.speculative.execution

Boolean

True

一个map任务运行缓慢时,是否开启一个备份

mapred.reduce.tasks.speculative.execution

Boolean

True

一个reduce任务运行缓慢时,是否开启一个备份

3.4对Map输出数据压缩(数据量大时)

        

属性名称

类型

默认值

说明

mapred.compress.map.output

Boolean

False

对map输出结果压缩

mapred.map.output.compression.codec

String

org.apache.hadoop.io.compress.DefaultCodec

编码格式

 

3.5Map端可调整的属性

属性名称

类型

默认值

说明

io.sort.mb

int

100

对Map端输出进行排序时所使用的内存大小,单位M

io.sort.record.percent

Float

0.05

io.sort.mb内存预留一部分给,来记录map的输出边界

io.sort.spill.percent

Float

0.8

io.sort.mb内存使用达到多少,将缓存数据写到磁盘

Io.sort.factor

int

10

排序文件时,一次合并的最大流数

Min.num.spills.for.combine

int

3

运行combiner所需要的最少文件数量(如果设置了combinber)

tasktracker.http.threads

Int

40

Tasktracker开启的数据复制线程数(赋值map数据给reduce)

3.6 redue端可调整的属性

属性名称

类型

默认值

说明

mapred.reduce.parallel.copies

int

5

用于将map输出复制到reducer的线程数量

Io.sort.factor

int

10

排序文件时,一次合并的最大流数

mapred.job.shuffle.input.buffer.percent

 

Float

0.70

整个堆空间的百分比,用于shuffle的赋值阶段

mapred.job.shuffle.merge.percent

 

Float

0.66

map输出缓存,使用达到这个比例,启动合并输出到磁盘

mapred.inmem.merge.threshold

Int

1000

启动合并,最大map输出量

mapred.job.reduce.input.buffer.percent

 

Float

0

在reduce过程中,用来在内存中保存map输出空间占整个堆空间的比例

 

Stream

属性名

属性值

描述

mapred.input.dir

 

hdfs://15.15.36.104:9000/wolf1

 

输入文件目录

mapred.input.format.class

org.apache.hadoop.mapred.TextInputFormat

 

mapred.mapper.class

org.apache.hadoop.streaming.PipeMapper

 

stream.map.streamprocessor

/bin/cat

Streammap函数

mapred.mapoutput.key.class

org.apache.hadoop.io.Text

Map函数输出key类型

mapred.mapoutput.value.class

 

org.apache.hadoop.io.Text

Map函数输出value类型

 

mapred.output.dir

 

hdfs://15.15.36.104:9000/wolf2

 

输出文件目录

mapred.output.format.class

org.apache.hadoop.mapred.TextOutputFormat

 

mapred.reducer.class

org.apache.hadoop.streaming.PipeReducer

 

stream.reduce.streamprocessor

/bin/cat

Stream reduce函数

mapred.output.key.class

org.apache.hadoop.io.Text

Reduce输出的Key类型

mapred.output.value.class

org.apache.hadoop.io.Text

 

Reduce输出的value类型

 

Pipes

属性名

属性值

描述

mapred.input.dir

hdfs://15.15.36.104:9000/temp

 

输入文件目录

mapred.input.format.class

org.apache.hadoop.mapred.pipes.PipesNonJavaInputFormat

 

mapred.map.runner.class

org.apache.hadoop.mapred.pipes.PipesMapRunner

 

mapred.mapoutput.key.class

org.apache.hadoop.io.Text

Map函数输出key类型

mapred.mapoutput.value.class

 

org.apache.hadoop.io.Text

Map函数输出value类型

 

mapred.output.dir

 

hdfs://15.15.36.104:9000/x

输出文件目录

mapred.output.format.class

org.apache.hadoop.mapred.lib.NullOutputFormat

 

mapred.reducer.class

org.apache.hadoop.mapred.pipes.PipesReducer

 

stream.reduce.streamprocessor

/bin/cat

Stream reduce函数

mapred.output.key.class

org.apache.hadoop.io.Text

Reduce输出的Key类型

mapred.output.value.class

org.apache.hadoop.io.Text

 

Reduce输出的value类型

 

0 0
原创粉丝点击