相关练习题代码见链接,代码是ipynb带有运行结果,配合查看更容易理解。
内容:
- MapReduce的基本原理
- Pyspark的基本数据结构RDD和DataFrame的创建和查询
1. MapReduce原理初步认识
说明例子:统计多个文件中单词的数量;
如果是单个文件的话,一般的做法是:遍历文件中每个单词,然后建立单词到数量的哈希映射(即map过程),这样就得到了每个单词的数量统计;简单理解为单兵作战。
现在问题变成有多个文件,数据量增加了,这种情况下如何快速高效地统计单词数量?
显然在单线程上能做的也就只有上面那么多了,自然地将目光并行计算,这就要涉及到多线程,分布式计算等内容。简单理解就是人多力量大,意思是:把任务分一分,然后每个人领一个任务计算,最后再把结果汇总。然而“人多”了自然就涉及到每个人如何相互协作,这个就是MapReduce要解决的问题。
粗略理解就是三个过程:Map -> intermediate values -> Reduce,也就是图1对应的Map -> shuffle -> Reduce;过程是这样:首先,对每个文件通过map函数将词和数量进行哈希映射,会得到很多临时的键/值对。然后,reduce函数合并这些具有相同键的临时键/值对1。
个人理解:
(1)建立单词到数量的哈希映射,这个过程是灵活的,你也可以建立单词到文件编号的映射等等,不论是建立什么到什么的映射,取其共性操作“映射(map)”来命名这个过程。
(2)合并必然带来临时的键/值对数量的减少,所以这个过程叫Reduce。
(3)从map过程的子任务之间是没有依赖关系的,可以分配给不同的线程或机器来完成。map,shuffle,reduce之间也是不存在严格的依赖关系的,也就是说shuffle没有必要等到整个map完全执行完成后才开始操作。从map到reduce上每条联通的路径都是一条独立的流水线(这种方式是DAG算子)。所以,map,shuffle,reduce也是可以分配给不同的线程或机器来完成。
(4)临时键/值对(intermediate values)可以存储在本地硬盘上,这就是Hadoop,也可以存储在内存中,这就是spark,当然spark还有更多改进的地方。

2. Spark基本数据结构:RDD(弹性分布式数据集)
RDD:resilient distributed dataset, 弹性指的是能够迅速复原的意思。我的理解,图1中的每个橙色的方框就是一个RDD对象。
在RDD的操作可以分为两种,一种是转换操作(transformation),另一种是行动操作(action)。在转化操作当中,spark不会为我们计算结果,而是会生成一个新的RDD节点,记录下这个操作。只有在行动操作执行的时候,spark才会从头开始计算整个计算2,这个就是所谓的惰性计算,可以大大提升计算效率。
2.1 RDD的转换和行动操作函数
序号 | 针对RDD值的转换操作 | 说明 |
---|---|---|
1 | .map(lambda row: int(row[16])) | 返回数字年份 |
2 | .flatMap(…) | 基本和map一样,不同的是返回结果上flatmap()永远是一维的,可以用来降维;和numpy的ravel()和flatten(),以及Keras的keras.layers.Flatten()有异曲同工之妙。此外,.flatMap(…)还可以用来过滤一些格式不正确的记录,因为他可以通过传递一个空列表来丢弃格式不正确的记录。 |
3 | .filter(…) | 过滤器,选择一些我们想要的数据。 |
4 | .distinct(…) | 和SQL中的相同,同样也是个高开销的方法,如无必要,慎用。 |
5 | .sample(…) | 返回RDD对象中的随机样本,一共三个参数,第一,指定采样是否应该替换,第二,样本占总体的比例,第三,伪随机数的种子; |
序号 | RDD常用行动操作 | 说明 |
---|---|---|
1 | .take(10) | 获取前10行的内容,该方法优于.collect() |
2 | .count() | 统计RDD对象中元素数量 |
3 | .reduce(…) | 使用用户指定的方法(可使用lambda表达式)减少RDD对象中的元素数量。通常接在map之后使用。 |
4 | .reduceByKey(…) | 按照key进行合并,通常接在有键/值对映射的map函数后,见统计单词 |
5 | .collect(…) | 将RDD对象所有元素以列表的形式返回。 |
例1:转换和行动的例子