![Hadoop大数据技术开发实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/392/27563392/b_27563392.jpg)
5.3 案例分析:单词计数
假如有这样一个例子,需要统计过去10年计算机论文中出现次数最多的几个单词,以分析当前的热点研究议题是什么。
这一经典的单词计数案例可以采用MapReduce处理。MapReduce中已经自带了一个单词计数程序WordCount,如同Java中的经典程序“Hello World”一样,WordCount是MapReduce中统计单词出现次数的Java类,是MapReduce的入门程序。
例如,输入内容如下的文件,要求计算出文件中单词的出现次数,且按照单词的字母顺序进行排序,每个单词和其出现次数占一行,单词与出现次数之间有间隔:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P89_10258.jpg?sign=1739182666-MwBhLeQvSe7ylxaX56u9PR7Fujvkebuc-0-d7f9bce3771df47fe6b0edea1075154a)
直接运行MapReduce自带的WordCount程序对上述文件内容进行计算即可,其计算结果如下:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P89_10257.jpg?sign=1739182666-9BWqDwNDRIs4Xyf2QMmffVmWjaPYtlB1-0-7749990dd2ce630e52a6fc59a4b936aa)
下面进一步对WordCount程序进行分析。
1. 设计思路
WordCount对于单词计数问题的解决方案为:先将文件内容切分成单词,然后将所有相同的单词聚集到一起,最后计算各个单词出现的次数,将计算结果排序输出。
根据MapReduce的工作原理可知,Map任务负责将输入数据切分成单词;Shuffle阶段负责根据单词进行分组,将相同的单词发送给同一个Reduce任务;Reduce任务负责计算单词出现次数并输出最终结果。
由于MapReduce中传递的数据都是<key,value>对形式的,而且Shuffle的排序、聚集和分发也是按照<key,value>对进行的,因此可将map()方法的输出结果设置为以单词作为key,1作为value的形式,表示某单词出现了1次(输入map()方法的数据则采用Hadoop默认的输入格式,即文件每一行的起始位置作为key,本行的文本内容作为value)。由于reduce()方法的输入是map()方法的输出聚集后的结果,因此格式为<key, value-list>,也就是<word, {1,1,1,1,…}>;reduce()方法的输出则可设置成与map()方法输出相同的形式,只是后面的数值不再是固定的1,而是具体计算出的某单词所对应的次数。
WordCount程序的执行流程如图5-5所示。
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P90_10557.jpg?sign=1739182666-FWFtdjHzYH3r1FQdLG1UTQgpEjjo4gmc-0-ffb73d99e315db28a005586504950f44)
图5-5 MapReduce单词计数执行流程
2. 程序源码
WordCount程序类的源代码如下:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P90_10418.jpg?sign=1739182666-ECck4lcKiOTx0gwkL5A2qNeUdi4NQPED-0-c98ce78dc42caa57341e6aa28e4d676e)
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P91_10858.jpg?sign=1739182666-JL96zkuMrK0wVGzfsjAVRaVTf6PLrRX9-0-779bbb932395508f3ccbde8eb1ce9fba)
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P92_11042.jpg?sign=1739182666-ugelQP0CzwaoBtcnkkNjcntUpEDVNDEH-0-572ad038c4a665ff4e15ed8ee9f36626)
3. 程序解读
Hadoop本身提供了一整套可序列化传输的基本数据类型,而不是直接使用Java的内嵌类型。Hadoop的IntWritable类型相当于Java的Integer类型,LongWritable相当于Java的Long类型,TextWritable相当于Java的String类型。
TokenizerMapper是自定义的内部类,继承了MapReduce提供的Mapper类,并重写了其中的map()方法。Mapper类是一个泛型类,它有4个形参类型,分别指定map()方法的输入key、输入value、输出key、输出value的类型。本例中,输入key是一个长整数偏移量,输入value是一整行单词,输出key是单个单词,输出value是单词数量。Mapper类的核心源码如下:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P92_11044.jpg?sign=1739182666-2yTYQPgr2OBX3PCDgOXHEGwwBdf0LCB6-0-1ffa2d467c31502f6441e2fe4a16bdaa)
同样,IntSumReducer是自定义的内部类,继承了MapReduce提供的Reducer类,并重写了其中的reduce()方法。Reducer类跟Mapper类一样,是一个泛型类,有4个形参类型,分别指定reduce()方法的输入key、输入value、输出key、输出value的类型。Reducer类的输入参数类型必须匹配Mapper类的输出类型,即Text类型和IntWritable类型。
本例中,reduce()方法的输入参数是单个单词和单词数量的集合,即<word, {1,1,1,1,…}>。reduce()方法的输出也必须与Reducer类规定的输出类型相匹配。Reducer类的核心源码如下:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P92_11045.jpg?sign=1739182666-4QHDAFhTX7nRmzUKBB7NWCC4jDYjWaJe-0-17f6917d1a2d936fe39486358d8f0aaa)
main()方法中的Configuration类用于读取Hadoop的配置文件,例如core-site.xml、mapred-site.xml、hdfs-site.xml等。也可以使用set()方法重新设置相关配置属性,例如重新设置HDFS的访问路径而不使用配置文件中的配置,代码如下:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P93_11173.jpg?sign=1739182666-4RWKxLQIrUn20uqCDncdkXplcxP0eTg8-0-63addf35a6048c9027e7ce2c10d56aa4)
main()方法中的job.setCombinerClass(IntSumReducer.class);指定了Map任务规约的类。这里的规约的含义是,将Map任务的结果进行一次本地的reduce()操作,从而减轻远程Reduce任务的压力。例如,把两个相同的“hello”单词进行规约,由此输入给reduce()的就变成了<hello,2>。实际生产环境是由多台主机一起运行MapReduce程序的,如果加入规约操作,每一台主机会在执行Reduce任务之前进行一次对本机数据的规约,然后再通过集群进行Reduce任务操作,这样就会大大节省Reduce任务的执行时间,从而加快MapReduce的处理速度。本例的Map任务规约类使用了自定义Reducer类IntSumReducer,需要注意的是,并不是所有的规约类都可以使用自定义Reducer类,需要根据实际业务使用合适的规约类,也可以自定义规约类。
4. 程序运行
下面以统计文本文件中的单词频数为例,讲解如何运行上述单词计数程序WordCount,操作步骤如下:
执行以下命令,在HDFS根目录下创建文件夹input:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P93_11174.jpg?sign=1739182666-848cZ4xJ3vefPQy5vOyhbNWvWI55e9rL-0-a6cfaea6ec1debb2e46dc5d6f7869b19)
在本地新建一个文本文件words.txt,向其写入以下单词内容:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P93_11175.jpg?sign=1739182666-HkO7QrN4GzqdmH67TwOFMHVgYjBYE47H-0-92362abf5642b423b3b6b882c8262a74)
执行以下命令,将words.txt上传到HDFS的/input目录中:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P93_11176.jpg?sign=1739182666-ynXYY9fVn7BDbXXTGg8g8rtOn6DVg4CG-0-9c2d19d192791f6b5c2ded3909fd501a)
进入Hadoop安装目录,执行以下命令,运行Hadoop自带的WordCount程序:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P93_11177.jpg?sign=1739182666-STguBm5X8iWWl1NJaMRPsJMVLVLts4c9-0-26810c9a1e37487df4a8a49bc79de2fc)
上述命令中的/input为数据来源目录,/output为结果数据存储目录。
需要注意的是,HDFS中不应存在目录/output,程序会自动创建,若存在则会抛出异常。
若控制台输出以下信息,表示程序运行正常:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P94_11457.jpg?sign=1739182666-76eYwFzoiNdza2r3oyQvmXFe79W3uv36-0-7f1b6282cbdff1b7376fc596b63d3459)
程序运行过程中也可以访问YARN ResourceManager的Web界面http://centos01:8088查看程序的运行状态,如图5-6所示。
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P95_11460.jpg?sign=1739182666-AM1w1njb7L0JJU18G1xrDTLGBvbtdKH1-0-d0de083d0000f047c963e401220fa1fe)
图5-6 YARN ResourceManager Web界面
程序运行的结果以文件的形式存放在HDFS的/output目录中,执行以下命令,查看目录/output中的内容:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P95_11508.jpg?sign=1739182666-UreoWVvyBEQQPLR0AMGvYCn6g0O3kiG6-0-6d58a823037abe90739b2387c9683cc6)
可以看到,/output目录中生成了两个文件:_SUCCESS和part-r-00000,_SUCCESS为执行状态文件,part-r-00000则存储实际的执行结果,如图5-7所示。
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P95_11485.jpg?sign=1739182666-gUJgSMGofRpuqJN3Q7YhiBfQi4iIqrwp-0-ad7b0774823ee764a3b665a61df8c636)
图5-7 查看结果文件
可以执行以下命令,将运行结果下载到本地查看:
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P95_11509.jpg?sign=1739182666-OOx85kNt7yBpEiaOE7BHynZwtBUFAUmK-0-06425b53c2a8f09eea45e5d6e431e082)
也可以执行以下命令,直接查看结果文件中的数据,如图5-8所示。
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P95_11510.jpg?sign=1739182666-cURhhxEaBYHjUfXvFOQ9SmyQeH3ctmoK-0-e9bc832435acfed88c396098407b1412)
![](https://epubservercos.yuewen.com/B7D09D/15825993105224906/epubprivate/OEBPS/Images/Figure-P95_11503.jpg?sign=1739182666-LjT0tJ7CVbCx57wRJfwDqqiQjH1YjYXi-0-d761ad875dfab0271d5038f0cefa8804)
图5-8 查看单词计数结果数据