WordCount的理解与MapReduce的执行过程

WordCount的入门

WordCount是最常见、最基本的一个需求,例如进行词频统计、用户访问记录统计。如果数据量非常小的情况下,使用单机、批处理的方式就可以很快得到结果。但是如果数据量非常大,数据量可能是10G、100G等等。这种情况下使用单机、批处理的方式就非常低效率。所以这个时候就需要借助于分布式的思想进行处理——使用集群进行处理。就拿词频统计来说,处理的过程步骤如下图。

Input就是将需要进行处理的数据输入,输入后会经过Spliting操作,将输入的数据进行切分,将众多的数据划分成不同的小块然后发送至不同的节点上,在节点上进行Mapping操作。Mapping操作后得到每个word以及value值,需要将这些结果再Shuffling操作。此操作后每个关键词会在一组,然后又发送到不同的节点进行统计(Reducing),统计后由一节点进行汇总。得到最后的wordcount结果。整个过程充分体现了分而治之的思想。

由上面的图片可知,WordCount需要我们自己实现的只有Mapping和Reducing两个部分,即对应Map和Reduce两个部分【这也是MapReduce框架的由来】。所以在实践时,需要先准备这两个部分的代码。我们就以简单的统计词频来实现【Python实现】。

# map.py
import sys
import re
p = re.compile(r"\w+")
for line in sys.stdin:
    ss = line.strip().split(" ")
    for s in ss:
        if p.findall(s)[0].len<1:
            continue
        s = p.findall(s)[0].lower()
       if s.strip() != "":
           print("%s\t%s"%(s,1)) 
# red.py

#!/usr/local/bin/python
import sys
current_word = None
sum = 0 

for line in sys.stdin:
    word,val = line.strip().split("\t")
    if current_word == None:
        current_word = word
   if current_word != word:
       print("%s\t%s"%(current_word,sum))
       current_word = word
       sum = 0
    sum += int(val)
print("%s\t%s"%(current_word,str(sum)))  

完成map和reduce的编写,如果是单机的其实就算完成了,在Linux可以直接通过命令即可完成WordCount的操作——cat xxx.data | python map.py | sort -k1 | python red.py。但是集群环境下使用Hadoop Streaming来创建和运行map/reduce作业。需要额外写脚本进行运行。

HADOOP_CMD = "/usr/local/src/hadoop-2.6.1/bin/hadoop"
STREAM_JAR_PATH = "/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/" +
                    "hadoop-streaming-2.6.1.jar"
INPUT_FILE_PATH = "/data/The_Man_of_Property.txt" 
OUTPUT_PATH="/output/wc"

# 如果OUTPUT_PATH在HDFS中存在则直接删除
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH 

$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH  \
    -output $OUTPUT_PATH \ 
    -mapper "python map.py" \
    -reduce "python red.py" \
    -file ./map.py \
    -file ./red.py   

在这段shell脚本中需要注意的是如果不增加”$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH “,在集群上运行时,如果HDFS存在这个文件夹则是不能运行的,因此在运行前将该目录进行删除,当然也要慎用rmr命令。另外,如果对Linux不熟悉的同学会很奇怪最后的配置有\,这其实就是为了防止命令参数过长,进行换行,标准说法叫做续行。运行脚本编写完成后,就可以"chmod +x run.sh"然后运行run.sh脚本即可。

然后查看HDFS输出文件夹下对应的内容。

在使用Hadoop Streaming进行集群运行时,你会发现我们并没有进行排序,因为这一部分操作,框架中已经帮我们实现。简单的一个wordCount就完成了,但是在平时工作中并不会这么简单,例如,你只需要统计文章中某些词或者不统计那些词,你只需要去修改Map阶段的代码。

MapReduce的执行流程

在MapReduce中有一个最重要的思想——分而治之。可以说了解这个思想就相当于掌握了一大部分。用一个最简单、大家最感兴趣的一个实例——数钱来解释:现有一堆钞票,各种面值都有,现在让你去数各种面值各有多少。

现在你怎么办?一个人数还是找人帮你数,那你肯定是找人帮你一起数,而且是每个人各数一堆钞票。这也侧面表示了两种策略——单点策略和分治策略。在单点策略下,你一个人数所有的钞票,数出各种面值有多少张,但是这样耗时可想而知,而且代价比较高。而分治策略下,先给每一个人分一堆钞票,每个人数出各自手上各种面值有多少张,然后再进行汇总,每个人负责统计一种面值。很显然这种分治策略更胜一筹。

真是在这样的思想下,MapReduce应用而生,专注于处理海量数据的分布式计算框架。在MapReduce中是使用HDFS作为存储,因为作为海量数据HDFS具有系统可靠性、可扩展性、并发处理以及注重数据处理的吞吐量。

红色框内是处于shuffle阶段,shuffle阶段主要负责将map端生成的数据传递给reduce。操作包括了partition(分区)、sort(排序)、spill to disk(溢写到磁盘)。Shuffle是整个的核心部分,受限于网络带宽。

简单概括MapReduce执行流程:InputSplit==>Map==>Map输出的数据放入环形溢写缓冲区==>Paration,Sort(快速排序),Spill to Disk(溢写到磁盘中)==>Merge on Disk(合并)==>Fetch==>Spill to Disk,Sort(归并排序),Merge==> Reduce ==> Output。

Map:每一个输入的切片都会对应一个Map。

环形溢写缓冲区:默认大小为100M,但是并不是等缓冲区达到100M才会去溢写到磁盘中,而是达到80M的时候就会进行溢写,写入到本地的磁盘中。

Paration,Sort(快速排序),Spill to Disk(溢写到磁盘中):Paration是根据Hash值将Map每个节点的结果映射给不同的Reduce。

Merge on Disk(合并):之所以需要数据合并,是因为需要减少数据写入磁盘的数据量,而且减少网络传输的数据量,有点儿类似于数据压缩。

Fetch:就是将数据从Map端传到指定的Reduce端,那么你会好奇Reduce怎么知道数据来源于哪一个Map?这是因为Map和Reduce在执行过程中都是在TaskTracker中,而TaskTracker时刻与JobTracker保持心跳,时时通讯。所以Reduce直接请求JobTracker就知道哪个Map上属于自己的数据,就会到对应的Map上抓取数据。

Spill to Disk,Sort(归并排序),Merge:在Shuffle的Reduce端也是需要进行缓存溢写以及排序,合并的操作,数据合并也是为了减少数据量,提高执行效率。

Reduce:汇总、聚合的过程。

Output:输出到HDFS上。

猜你喜欢

转载自blog.csdn.net/qq_35363507/article/details/116594029