学长之前用Java写了一个程序,有两个文档,其中一个文档是regular expression,大概有8万行,每一行是一个regular expression,我们称之为pattern;另一个文档其实是文件夹里的一个,该文件夹里的所有文档都是从twitter上抓取下来的数据,数量从7万~30万不等。文件夹中的每一个文档代表一个topic,共有8个文档,即8个topic.
目的:用每一个pattern去匹配每一个topic中的每一条tweet,统计每个pattern在每个topic中出现的次数。
以上问题跟统计词频类似。程序用的是常规的嵌套循环,如下:
for (each Topic){ // 8 topicsfor (each LineOfTopic){ // average 200,000 lines
for (each pattern){ // about 80,000 patterns
while (match){
patternCount++; // count the frequency of the pattern in this topic
}
}
}
}
一共有8个topic,每个topic的行数平均约20万,pattern数量约8万,以上代码一共需要循环8×200000×80000=1.28×1011次,即100多亿次。同学5天前开始跑,到现在还没跑完。慢的原因大概有两个:
1. 循环次数太多
2. 正则表达式的匹配比较耗费时间
整个程序看起来跟MapReduce的经典例子WordCount很像,于是我觉得用MapReduce的方法可以优化一下。但其实,我没有学过Java,也没有学过并行运算。算是学过MapReduce,在Data Mining的课上有MapReduce的讲解和上机作业。但是上机讲解我有事没有去上课,然后我就去爬山了,等到下山当天才发现第二天就是MapReduce作业的deadline…
MapReduce与SparkApache Spark是由加州大学伯克利分校AMPLab开发的用于处理大数据处理的框架,可以在上面实现MapReduce. spark是『闪光,闪烁』的意思,表示Spark处理数据其实很快哦!
据资料讲,Spark比Hadoop更高,更快,更强。之前Data Mining课程上我们实现MapReduce应用时用的是Hadoop,第二年老师就换成Spark了。
没有打算在这里介绍MapReduce和Spark,因为我也不是很懂。我参考的资料并且觉得比较好的有下面这些:
Hadoop MapReduce原理学习详细讲解了MapReduce的原理与运作过程。
Spark Cluster Mode Overview
官方文档,介绍了Spark的基本原理,可以对Saprk的框架有一个大概的了解。
Hadoop安装教程 Ubuntu 14.04
讲解了如何在Ubuntu上安装hadoop,步骤很详细。因为Saprk是基于Hadoop的,因此安装Spark之前要先安装Hadoop.
Spark安装与基础使用 Ubuntu 14.04
详细讲解了如何在Ubuntu上安装Spark.
Learning Spark - Working with Key/Value Pairs
Spark使用了一种抽象的数据类型RDD (Resilient Distributed Datasets, 弹性分布数据集),各种操作都是基于RDD进行。Spark将其分发到集群的各个结点上进行并行操作。Spark中一种经常使用到的数据结构是Key/Value结构,这些数据结构也是RDD,称为pair RDD. 这篇文档详细讲解了pair RDD的各种操作。
Saprk Programming Guide
介绍Saprk基本的开发流程与步骤。
一个Saprk应用是b运行包含了一个在用户定义的main函数中的驱动程序(driver program),然后在集群(cluster)上并行执行各种操作。driver program由SaprkContext对象定义,所以要使用Spark首先要用SparkContext创建一个driver program,然后才在该驱动程序上运行cluster. 而运行cluster时Spark要先连接Cluster Manager来分配资源。一旦连接上Cluster Manager,Saprk会在这个cluster的每一个节点(node)上建立executor. executor是用于处理数据计算数据存储数据的处理器(processor)。之后Spark会将用户写的代码发送到各个节点的executor上。这时,executor有了代码就可以执行各种操作了,Spark让executor执行指定操作(由代码决定)的过程叫做任务(task)。下图是Spark执行一个任务的框架图。

程序优化
创建Spark on Java
我没有学过Java,因此只能一步一步照着各种文档操作。Spark on Java是通过Maven建立的(Maven是什么?我不懂……)。创建Maven时官方文档说需要在pom.xml中加上如下信息:
org.apache.sparkspark-core_2.112.0.0但我使用NetBeans 8.1添加以上信息后编译没有通过,后来网上查到还需要在pom.xml中的properties节点下添加以下内容才可以(不知道原因):
2.0.0.version>首先在使用到Spark的函数中创建driver program.
SaprkConf conf = new SaprkConf().setAppName("countPatternPerTopic").setMaster("local[4]");JavaSparkContext sc = new JavaSparkContex(conf);
上面代码中的变量sc就是driver program啦!
读取数据Spark读取数据很简单:
JavaRDD patternRDD = sc.textFile(filePath);注意到text是一个RDD. 以上代码会将文本一次性全部读入内存,然后自动按行分割。
map与reduce操作map操作将数据分为若干子数据集,使得Spark可以将这些数据集分配到不同的节点上执行并行操作。执行map操作后返回的是RDD数据类型。Spark的map操作有很多种,这里用的是mapToPair,返回的是一个pair RDD,即Key/Value结构。
JavaPairRDD PattPair = patternRDD.mapToPair(user_defined_func);,>上述代码中的JavaPairRDD表示这个pair RDD的key是String类型,即pattern名称;value是int类型,即pattern的出现次数。user_defined_func是自己定义的map算子,它的输入默认是文本的每一行。,>
我的问题是:有一个pattern文档,有若干tweet文档,计算pattern文档中的每一个pattern在每一个tweet文档中出现次数。相当于是给定一个word list,统计这个word list中的每个单词的词频。
如果只是统计word list的词频,那比较简单,可以直接用Spark的WordCount例子统计出词频,然后再用Spark提供的filter函数过滤掉不在word list中的单词即可。
但目前并不是简单统计词频,这个任务还有一个正则表达式匹配的过程。最开始的想法是:对pattern文档做一个map操作,得到Pattern RDD,同时生成tweet文档的Tweet RDD,这样得到两个RDD,然后在Pattern RDD的map函数中对tweet的RDD做统计。像是这样:
上图中,分别生成了两个RDD,但只对Pattern RDD做map和reduce操作,且在Pattern RDD的map中引用了Tweet RDD. 注意到Saprk中所有的操作都是在抽象数据类型RDD中进行,因此map和reduce其实是RDD的对象(上面的代码中可以看到),因此图中的map操作实际上是在Pattern RDD中引用了Tweet RDD.
听起来好完美,然而并不是。Saprk中RDD不可以嵌套,具体来说就是不可以在map或reduce操作中引用另一个RDD