Hadood之MapReduce的介绍及简单例子

        ~~~~~~~        上课时听老师讲了这部分的内容,也通过老师的代码完成了想实现的操作,但是对于MapReduce以及这里面的代码是怎么实现的还不是很理解,故写下此篇博客

“It’s not a bug, it’s a feature.”

1. 到底什么是Mapper与Reducer

        ~~~~~~~        首先给出定义:Mapper与Reducer是MapReduce编程模型的两个核心组件,用于处理大规模数据集的并行计算

Mapper(映射器):Mapper的主要任务是将输入数据分割成较小的数据块,并为每个数据块生成键值对。通常情况下,Mapper可以独立地处理输入数据的子集,因此它可以在分布式环境中并行执行。Mapper根据特定的逻辑处理输入数据,并生成一系列中间键值对作为输出。这些中间键值对将作为输入传递给Reducer。

Reducer(归约器):Reducer接收来自Mapper的中间键值对,并将它们按照键进行分组和聚合。Reducer的主要任务是对具有相同键的键值对进行归约操作,以便生成最终的输出结果。Reducer可以并行处理不同组的键值对,因此可以在分布式环境中并行执行。归约操作可以是简单的聚合、计数、求和等操作,也可以是更复杂的业务逻辑处理。

整个MapReduce任务的工作流程如下:

  1. 输入数据被划分成若干个数据块。
    Hadoop集群中的Mapper节点并行地处理各个数据块,将输入数据转换为中间键值对。
    中间键值对根据键进行分组,并发送给Reducer节点。
  2. Reducer节点并行地处理不同组的键值对,执行归约操作,生成最终的输出结果。
  3. 输出结果被存储在Hadoop分布式文件系统(如HDFS)中或传输给其他系统进行后续处理或分析。

        ~~~~~~~        MapReduce模型的优势在于它能够有效地处理大规模数据,并利用分布式计算的优势进行并行处理。Mapper和Reducer是这个模型中的关键组件,负责将任务拆分成可并行处理的部分,并最终生成最终的结果。

2. 为什么要用MapReduce而不是普通的代码

hive spark hbase
hadoop spark
mysql
hadoop spark mysql

        ~~~~~~~        如果有以上这样的一段文本,需要你统计每一个单词出现了几次,你可以会说我肉眼就能统计出来了,可如果是成千上万呢?
        ~~~~~~~        又或许有的人会说那我直接写一段python或者C++来统计个数不可以吗?也可以但是Hadoop的MapReduce绝对是更优的选择,这里简单的扩展一下为什么使用MapReduce
,也就是MapReduce模型相对于串行计算的主要区别:

  • 可扩展性:MapReduce模型是为处理大规模数据而设计的,可以方便地在分布式环境中运行。它可以将任务划分成多个独立的子任务,并在多台计算机上并行执行,从而提高计算速度和处理能力。而串行计算通常无法有效地处理大规模数据集

  • 容错性:MapReduce模型提供了内建的容错机制。当某个节点出现故障时,MapReduce框架会自动重新调度任务,保证计算的正确性和鲁棒性。而在串行计算中,单个节点的故障可能导致整个计算中断,需要手动处理错误和恢复。

  • 数据局部性:MapReduce模型通过将计算任务分发到存储数据所在的节点上,利用数据局部性减少了数据传输的开销。在大规模数据处理中,数据传输往往是性能瓶颈之一。而串行计算通常需要将数据从存储位置复制到计算节点,增加了数据传输的开销。

  • 并行性:MapReduce模型充分利用了并行计算的优势。通过将任务划分为多个独立的Mapper和Reducer任务,可以在分布式环境中同时执行多个任务,从而提高整体计算速度。而在串行计算中,计算任务依次顺序执行,无法充分利用多核处理器或分布式系统的并行能力。

        ~~~~~~~        尽管可以使用Python或C++等编程语言编写串行代码来计算单词出现次数,但当面对大规模数据时,串行计算可能会遇到性能瓶颈和可扩展性问题。MapReduce模型提供了一种分布式、可扩展和容错的计算模型,使得处理大规模数据更加高效和可靠。

3. 常见的MapReduce任务示例

3.1 简单的单词计数任务

        ~~~~~~~        那这里我们就介绍一下如何使用MaperReduce实现上述目标
首先是“mapper.py”文件

#!/usr/bin/env python
#encoding=utf8

        ~~~~~~~        这部分是脚本的开头部分,它通常用于指定脚本的解释器和字符编码

import sys

        ~~~~~~~        导入sys模块,sys模块提供了与Python解释器和系统操作相关的功能

for line in sys.stdin:

        ~~~~~~~        这行代码使用sys.stdin来读取输入的内容,并将每一行赋值给变量line。for循环用于遍历输入中的每一行

    line = line.strip()

        ~~~~~~~        这行代码使用strip()函数去除字符串中的空白字符,包括换行符。它将处理后的行重新赋值给line变量

    words = line.split(" ")

        ~~~~~~~        这行代码使用for循环遍历words列表中的每个单词,并将当前单词赋值给变量word

        print("{0}\t{1}".format(word, 1))

        ~~~~~~~        这行代码使用print()函数打印输出结果。"{0}\t{1}"是一个格式化字符串,其中{0}表示第一个参数word,{1}表示第二个参数1。format()函数将这些参数插入到格式化字符串的相应位置,生成最终的输出字符串。这行代码的作用是以<单词>\t<出现次数>的格式打印每个单词和它出现的次数。

以下为mapper文件的完整代码:

for line in sys.stdin:
    line = line.strip()
    #拆分单词
    words = line.split(" ")
    #计算每个单词出现次数,转换成Key-Value类型
    for word in words:
        print("{0}\t{1}".format(word,1))

下面来看reducer.py文件

current_key = None
current_key_sum = 0

        ~~~~~~~        定义两个变量current_key和current_key_sum,初始值分别为None和0。这些变量用于追踪当前处理的键值和键值的总和

for line in sys.stdin:

        ~~~~~~~        使用sys.stdin读取标准输入的每一行,并将其赋值给变量line。这个循环将迭代输入的每一行

    key, value = line.split("\t", 1)
    value = int(value)

        ~~~~~~~        将line字符串使用\t(制表符)进行分割,得到键值对中的键和值,并将它们分别赋值给key和value变量。然后,将value转换为整数类型 1 是作为 split 方法的第二个参数传递的,这个参数指定了最大的分割次数

    if current_key is None:
        current_key = key
    if current_key == key:
        current_key_sum += value
    else:
        print("{0}\t{1}".format(current_key, current_key_sum))
        current_key = key
        current_key_sum = value

        ~~~~~~~        这段代码处理键变化的情况。当当前键key与之前的键current_key不相同时,将之前的键current_key和对应的累计值current_key_sum打印输出。然后,更新当前的键为key,并将累计值current_key_sum重新赋值为当前值value

print("{0}\t{1}".format(current_key, current_key_sum))

        ~~~~~~~        这行代码用于打印最后一个键current_key以及对应的累计值current_key_sum。这行代码位于循环之外,保证最后一个键的统计结果也能被输出

        ~~~~~~~        以下是reducer.py的完整代码

#!/usr/bin/env python
# encoding=utf8
import sys

#当前统计的单词
current_key = None
current_key_list = []

for line in sys.stdin:
    line = line.strip()
    try:
        (key, location) = line.split("\t")
#        print(key,location)
        if key is None or location is None:
            continue
        if key == current_key:
            current_key_list.append(location)
        else:
            if current_key:
                print("{0}\t{1}".format(current_key, ' '.join(current_key_list)))
            current_key = key
            current_key_list = [location]
    except:
         pass
print("{0}\t{1}".format(current_key, ' '.join(current_key_list)))

3.1.1 自动排序功能

        ~~~~~~~        其实上述的代码笔者看了很久都觉得是错的,因为前一个的key与当前的key不一样就会输出,那么根本不能统计数据,但是hadoop运行出来的结果却是正确的,所以笔者经过请教他人才知道:

        ~~~~~~~        在标准的MapReduce框架中,Map和Reduce阶段之间的数据是经过排序的。具体来说,Map阶段的输出会被自动按照键(key)进行排序,然后传递给Reduce阶段。这种自动排序是由MapReduce框架内部实现的,而不需要开发者显式编写排序逻辑。
        ~~~~~~~        在MapReduce中,默认使用的是字典序(lexicographic order)对键进行排序。如果你需要使用其他的排序方式,可以通过自定义排序函数(Comparator)来实现。Comparator会根据你指定的排序规则对键进行排序。

        ~~~~~~~        还有这么一回事,所以这段代码也就能够正确处理数据了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值