Hadoop教程(四):理解MapReduce、MapReduce计数器和连接、MapReduce Hadoop程序连接数据

本教程中的代码分为 3 个部分:

解释 SalesMapper 类

解释 SalesCountryReducer 类

解释 SalesCountryDriver 类

SalesMapper类的说明

在本节中,我们将了解 SalesMapper 类的实现。

我们首先指定类的包名称。 SalesCountry 就是这个示例中使用的包名。请注意编译的输出,SalesMapper.class 将进入目录并命名这个软件包名称:SalesCountry.

其次,我们导入库软件包。

以下快照显示实现 SalesMapper 类 

代码解释:

1. SalesMapper 类定义

public class SalesMapper extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable> {...}

每一个 mapper 类必须从 MapReduceBase 类进行扩展,它必须实现 Mapper 接口。

2. 定义 'map' 函数

1
2
3
4
publicvoidmap(LongWritable key,
         Text value,
OutputCollector<Text, IntWritable> output,
Reporter reporter) throwsIOException

Mapper类的主要部分是接受四个参数的 “map()” 方法。

每次调用 'map()' 方法, 一个键值 key-value 对 ('key' 和 'value' 在代码里) 被传递。

'map()' 方法开始被接受拆分输入文本作为一个参数,并使用分词来拆分这些行成词。

1
2
String valueString = value.toString();
String[] SingleCountryData = valueString.split(",");

这里,“,” 被用作分隔符。

在这之后,使用记录在数组  'SingleCountryData' 中的第七索引,其值为 '1'.

        output.collect(new Text(SingleCountryData[7]), one);

我们在选择第7索引记录,因为我们需要的国家数据,它位于数组 'SingleCountryData' 的第七索引。

请注意,我们输入的数据是下面的格式 (Country 在索引的位置为:7,  0 是开始的索引)-

Transaction_date,Product,Price,Payment_Type,Name,City,State,Country,Account_Created,Last_Login,Latitude,Longitude

mapper的输出使用的是 'OutputCollector' 的 'collect()' 方法的键值对.

SalesCountryReducer 类的说明

在本节中,我们将了解 SalesCountryReducer 类的实现。

1. 我们首先为类指定包的名称。SalesCountry 是包的名称。请注意编译的输出, SalesCountryReducer.class 将进入命名这个软件包名称目录: SalesCountry.

其次,我们导入库软件包。

以下快照显示实现 SalesCountryReducer 类

代码解释:

1. SalesCountryReducer 类定义 -

public class SalesCountryReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {

此处,前两个数据类型, 'Text' 和 'IntWritable' 是输入键值的数据类型到reducer。

映射器的输出的形式<CountryName1, 1>, <CountryName2, 1>.映射器的输出被输入到reducer。所以,以配合其数据类型, Text 和 IntWritable 数据在这里输入被使用。

最后两个数据类型,'Text' 和 'IntWritable' 是由 reducer 的键 - 值对的形式生成的输出的数据类型。

每个 reducer 类必须从MapReduceBase类进行扩展,它必须实现 Reducer 接口。

2. Defining 'reduce' function-

1
2
3
publicvoidreduce( Text t_key,
             Iterator<IntWritable> values,  OutputCollector<Text,IntWritable> output,
             Reporter reporter) throwsIOException {

输入到 reduce() 方法是在具有多个值的列表中选择一个键。

例如,在我们的示例中,这将是 -

<United Arab Emirates, 1>, <United Arab Emirates, 1>, <United Arab Emirates, 1>,<United Arab Emirates, 1>, <United Arab Emirates, 1>, <United Arab Emirates, 1>.

这赋予 reducer 作为 <United Arab Emirates, {1,1,1,1,1,1}>

因此,接受这种形式参数,前两个数据类型的使用,即 Text 和 Iterator<IntWritable>. Text是一个数据类型的键 和 Iterator<IntWritable>为对于键的值的列表的数据类型。

接下来的参数的类型是 OutputCollector<Text,IntWritable> 它收集 reducer 阶段的输出。

reduce() 方法开始通过复制键值和初始化频率计数为0。

        Text key = t_key;
        int frequencyForCountry = 0;

然后,使用 “while” 循环,我们通过与键关联的值列表循环,并通过总结所有计算的值。

1
2
3
4
5
6
while(values.hasNext()) {
           // replace type of value with the actual type of our value
           IntWritable value = (IntWritable) values.next();
           frequencyForCountry += value.get();
           
       }

现在,结果中的键得到的频率计数输出到收集器。

下面的代码执行这个 -

        output.collect(key, new IntWritable(frequencyForCountry));

SalesCountryDriver类的说明

在本节中,我们将了解 SalesCountryDriver 类实现。

1. 我们首先为类指定包的名称。 SalesCountry 是这里使用的包名。请注意编译的输出, SalesCountryDriver.class 将进入命名这个包名称的目录: SalesCountry.

这里一行指定是包名称后面的代码是导入库软件包。

2. 定义一个用于创建一个新的客户端工作,配置 Mapper及Reducer 类对象驱动程序类。

该驱动程序类负责设置我们的 MapReduce 作业在 Hadoop 运行。 在这个类中,我们指定作业名称,输入/输出,mapper 和 reducer 类名称的数据类型。

3. 在下面的代码片段中,我们设置这是用来输入数据集消费和生产输出,分别输入和输出目录。

arg[0] 和 arg[1] 是通过 MapReduce 的实际操作,也就是赋予在命令行参数执行命令,

$HADOOP_HOME/bin/hadoop jar ProductSalePerCountry.jar /inputMapReduce /mapreduce_output_sales


4. 触发我们的作业

下面的代码开始执行 MapReduce 作业

try{
           // Run the job
           JobClient.runJob(job_conf);
       } catch(Exception e) {
           e.printStackTrace();
       }




在MapReduce的计数器是用于收集关于 MapReduce 工作的统计信息的机制。这个信息在MapReduce的作业处理的问题的诊断是很有用的。 计数器类似于将在 map 或 reduce 在代码日志信息中。

通常情况下,这些计数器在一个程序(map 或 reduce)中定义,当一个特定事件或条件(特定于该计数器)发生执行期间递增。计数器是一个很好的应用来从输入数据集跟踪有效和无效的记录。

有两种类型的计数器:

1. Hadoop 内置计数器: 有一些内置计数器存在每个作业中。下面是内置计数器组:

  • MapReduce任务计数器 - 收集任务的具体信息(例如,输入记录的数量)在它的执行期间。
  • 文件系统计数器 - 收集信息像由一个任务读取或写入的字节数
  • FileInputFormat计数器 - 收集通过FileInputFormat读取的字节数的信息
  • FileOutputFormat计数器 - 收集的字节数量的信息通过 FileOutputFormat 写入
  • Job 计数器- 这些计数器使用 JobTracker。它们收集统计数据包括如,任务发起了作业的数量。

2. 用户定义的计数器

除了内置的计数器,用户可以定义自己的计数器,通过使用编程语言提供了类似的功能。 例如,在 Java 的枚举用于定义用户定义的计数器。

一个MapClass例子使用计数器计算缺失和无效值的数量:

 
publicstaticclassMapClass
            extendsMapReduceBase
            implementsMapper<LongWritable, Text, Text, Text>
{
    staticenumSalesCounters { MISSING, INVALID };
    publicvoidmap ( LongWritable key, Text value,
                 OutputCollector<Text, Text> output,
                 Reporter reporter) throwsIOException
    {
        
        //Input string is split using ',' and stored in 'fields' array
        String fields[] = value.toString().split(",", -20);
        //Value at 4th index is country. It is stored in 'country' variable
        String country = fields[4];
        
        //Value at 8th index is sales data. It is stored in 'sales' variable
        String sales = fields[8];
      
        if(country.length() == 0) {
            reporter.incrCounter(SalesCounters.MISSING, 1);
        } elseif(sales.startsWith("\"")) {
            reporter.incrCounter(SalesCounters.INVALID, 1);
        } else{
            output.collect(newText(country), newText(sales + ",1"));
        }
    }
}

上面的代码片段显示在 Map Reduce 实现计数器的示例。

在这里,SalesCounters是用“枚举”定义的计数器。它被用来计算 MISSING 和 INVALID 的输入记录。

在代码段中,如果 “country” 字段的长度为零那么它的值丢失,因此相应的计数器 SalesCounters.MISSING 递增。

接下来,如果 “sales” 字段开头是符号 '' ,则记录被视为无效。这通过递增计数器 SalesCounters.INVALID 来表示。

MapReduce 连接

连接两个大的数据集可以使用 MapReduce Join 来实现。然而,这个过程需要编写大量的代码来执行实际的连接操作。

连接两个数据集开始是通过比较每个数据集的大小。如果因为相比其他数据集一个数据集小,那么小数据集被分布到集群中的每个数据节点。一旦分散,无论是 Mapper 或 Reducer 使用更小的数据集进行查找匹配的大型数据集的记录,然后结合这些记录,形成输出记录。

这取决于在实际连接进行的地方,这个连接分为:

1. 映射端连接 - 当该联接是由映射器执行的,它称为映射端链接。在这种类型中,联结前的数据由映射函数实际来消耗的处理。它是强制性的,输入到每个映射是在分区中的形式,并且是按排序顺序。另外,必须有一个相等数目的分区,它必须由连接键进行排序。

2. Reduce端连接- 当连接是通过减速器进行的,称为reduce端连接。没有必要在此连接有数据集中在以结构化形式(或分区)。

在这里,映射端的处理发出连接这两个表的关键字和对应的元组。作为该处理的效果,所有的元组相同连接键都落在相同的 reducer,然后使用相同的连接键连接记录。

整体处理流程示于下图。




这里有两个数据集合在两个不同的文件中,如下所示:

 
 

DEPT_ID 键在这两个文件中常见的。

目标是使用 MapReduce 加入来组合这些文件。

输入: 我们的输入数据集是两个txt文件:DeptName.txt 和 DepStrength.txt

下载输入文件

前提条件:

  • 本教程是在 Linux 上开发 - Ubuntu操作系统
  • 已经安装的Hadoop(本教程使用2.7.1版本)
  • Java的开发运行环境已经在系统上安装(本教程使用的版本是:1.8.0)

在我们开始实际操作之前,使用的用户 'hduser_'(使用 Hadoop 的用户)。

yiibai@ubuntu:~$ su hduser_

 

步骤

Step 1) 复制 zip 文件到您选择的位置

hduser_@ubuntu:/home/yiibai$ cp /home/yiibai/Downloads/MapReduceJoin.tar.gz /home/hduser_/
hduser_@ubuntu:/home/yiibai$ ls /home/hduser_/

操作过程及结果如下:

 

Step 2) 解压缩ZIP文件,使用以下命令:

hduser_@ubuntu:~$ sudo tar -xvf MapReduceJoin.tar.gz

Step 3) 进入目录 MapReduceJoin/

hduser_@ubuntu:~$ cd MapReduceJoin/

Step 4)  启动 Hadoop

hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/sbin/start-dfs.sh
hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/sbin/start-yarn.sh

Step 5) DeptStrength.txt 和 DeptName.txt 用于此项目的输入文件

这些文件需要使用以下命令 - 复制到 HDFS 的根目录下,使用以下命令:

hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/bin/hdfs dfs -copyFromLocal DeptStrength.txt DeptName.txt /

Step 6) 使用以下命令 - 运行程序

hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/bin/hadoop jar MapReduceJoin.jar /DeptStrength.txt /DeptName.txt /output_mapreducejoin

Step 7)

在执行命令后, 输出文件 (named 'part-00000') 将会存储在 HDFS目录 /output_mapreducejoin 

结果可以使用命令行界面可以看到:

hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/bin/hdfs dfs -cat /output_mapreducejoin/part-00000

结果也可以通过 Web 界面查看(这里我的虚拟机的IP是 192.168.1.109),如下图所示:

现在,选择 “Browse the filesystem”,并浏览到 /output_mapreducejoin

打开 part-r-00000

结果如下所示,点击 Download 链接下载:

打开下载后的 文件,结果如下所示:

注:请注意,下一次运行此程序之前,需要删除输出目录 /output_mapreducejoin

$HADOOP_HOME/bin/hdfs dfs -rm -r /output_mapreducejoin

另一种方法是使用不同的名称作为输出目录。

from: http://www.yiibai.com/hadoop/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值