和 Hadoop 一样,Spark 提供了一个 Map/Reduce API(分布式计算)和分布式存储。二者主要的不同点是,Spark 在集群的内存中保存数据,而 Hadoop 在集群的磁盘中存储数据。
大数据对一些数据科学团队来说是 主要的挑战,因为在要求的可扩展性方面单机没有能力和容量来运行大规模数据处 理。此外,即使专为大数据设计的系统,如 Hadoop,由于一些数据的属性问题也很难有效地处理图数据,我们将在本章的其他部分看到这方面的内容。
Apache Spark 与 Hadoop 类似,数据分布式存储在服务器的集群或者是“节点”上。 不同的是,Spark 将数据保存在内存(RAM)中,Hadoop 把数据保存在磁盘(机械 硬盘或者 SSD 固态硬盘)中。
定义 :在图和集群计算方面,“节点”这个词有两种截然不同的意思。 图数据由顶点和边组成,在这里“节点”与顶点的意思相近。在集群计算 方面,组成集群的物理机器也被称为“节点”。为避免混淆,我们称图的 节点为顶点,这也是 Spark 中的专有名词。而本书中的“节点”这个词我 们严格定义为集群中的单个物理计算节点。
大数据因为数据量大单机无法处理。Hadoop 和 Spark 都是把数据分布在集群节点上的分 布式框架中。Spark 把分布式数据集存放在内存中,所以比 Hadoop 把数据存放在磁盘中 处理速度要快很多。
除了将要计算的数据保存的位置不同(内存和磁盘),Spark 的 API 比 Hadoop的 Map/Reduce API 更容易使用。Spark 使用简洁且表达力较好的 Scala 作为原生编程语言,写 Hadoop Map/Reduce 的 Java 代码行数与写 Spark 的 Scala 的代码行的数 量比一般是 10:1。
虽然本书主要使用 Scala,但是你对 Scala 不熟悉也不用担心,我们在第 3 章提 供了快速入门,包括怪异、晦涩和简练的 Scala 语法。进一步熟悉 Java、C++、C#、 Python 等至少一门编程语言是必要的。
模糊的大数据定义
现在的“大数据”概念已经被很大程度地夸大了。大数据的概念可以追溯到Google 在 2003 年发表的 Google 文件系统的论文和 2004 年发表的 Map/Reduce 论文。
大数据这个术语有多种不同的定义,并且有些定义已经失去了大数据所应有的意 义。但是简单的核心且至关重要的意义是:大数据是因数据本身太大,单机无法处理。
数据量已经呈爆炸性增长。数据来自网站的点击、服务器日志和带有传感器的 硬件等,这些称为数据源。有些数据是图数据(graph data),意味着由边和顶点组成, 如一些协作类网站(属于“Web 2.0”的社交媒体的一种)。大的图数据集实际上是 众包的,例如知识互相连接的 Wikipedia、Facebook 的朋友数据、LinkedIn 的连接数 据,或者 Twitter 的粉丝数据。
Hadoop :Spark 之前的世界
在讨论 Spark 之前,我们总结一下 Hadoop 是如何解决大数据问题的,因为Spark 是建立在下面将要描述的核心 Hadoop 概念之上的。
Hadoop 提供了在集群机器中实现容错、并行处理的框架。Hadoop 有两个关键 能力 :
- HDFS—分布式存储
- MapReduce—分布式计算
HDFS 提供了分布式、容错存储。NameNode 把单个大文件分割成小块,典型 的块大小是 64MB 或 128MB。这些小块文件被分散在集群中的不同机器上。容错性 是将每个文件的小块复制到一定数量的机器节点上(默认复制到 3 个不同节点, 下图中为了表示方便,将复制数设置为 2)。假如一个机器节点失效,致使这个机器上的 所有文件块不可用,但其他机器节点可以提供缺失的文件块。这是 Hadoop 架构的 关键理念 :机器出故障是正常运作的一部分。
三个分布式数据块通过 Hadoop 分布式文件系统(HDFS)保持两个副本。
MapReduce 是提供并行和分布式计算的 Hadoop 并行处理框架,如下图 。
MapReduce 是被 Hadoop 和 Spark 都用到的一个数据处理范式。图中表示计算服务器日 志文件中“error”出现的次数,这是一个 MapReduce 操作。通常 Map 操作是一对一的 操作,对每一个源数据项生成一个相应的数据转换操作。Reduce 是多对一的操作,聚合 Map 阶段的输出。Hadoop 和 Spark 都用到了 MapReduce 范式。
用 MapReduce 框架,程序员写一个封装有 map 和 reduce 函数的独立代码片段来处 理 HDFS 上的数据集。为取到数据位置,代码打包(jar 格式)分发到数据节点, Map 操作就在这些数据节点上执行,这避免了集群的数据传输导致消耗网络带宽。 对于 Reduce 聚合操作,Map 的结果被传输到多个 Reduce 节点上做 reduce 操作(称 之为 shuffling)。首先,Map 阶段是并行操作的,Hadoop 提供了一个弹性机制,当 一个机器节点或者一个处理过程失败时,计算会在其他机器节点上重启。
MapReduce 编程框架将数据集抽象为流式 key-value 键值对,然后处理这些键 值对并写回到 HDFS。这是一个有局限的范式,但它已被用来解决许多数据并行问题, 用链接在一起的 MapReduce 进行“读-处理-写”操作过程。对于一些简单的任务,上图显示的是比较适合的场景。但是对于一些如机器学习算法中的迭代计算算 法,用这种 MapReduce 范式就很痛苦,这也是选择使用 Spark 的原因。
Spark :内存中的 MapReduce 处理
我们来看另一个可选的分布式处理系统,构建在 Hadoop 基础之上的 Spark。在这一小节你会了解到,在 Spark 处理图数据时扮演重要角色的弹性分 布式数据集(RDD)导致 Hadoop 衰落的两类问题是 :
- 交互式查询
- 迭代算法
Hadoop 很适合在一个大的数据集上做单次查询,而在许多实际场景中,一旦有 了一个想要的答案,我们就想再问数据一个问题,这就是交互式查询。使用 Hadoop 的话,就意味着要等待重新从磁盘中加载数据,再次处理数据。我们不得不执行一 组相同的计算作为随后分析的前提,这不符合常理。
迭代算法已经被广泛应用于机器学习任务,如随机梯度下降算法,以及之后 会看到的 PageRank 这类图计算算法。迭代算法是在一个数据集上一遍又一遍地做 一组计算,直到满足一个标准(循环结束条件)才结束迭代。 在 Hadoop 中实现这种算法,一般需要一系列加载数据的 MapReduce 任务,这些 MapReduce 任务要在 每一个迭代过程中重复运行。对于非常大的数据集,每个迭代过程要花费 100 秒或1000 秒,整个迭代过程非常耗时。
下面你会看到 Spark 如何解决这些问题。如 Hadoop 一样,Spark 也是运行在 一个常见的硬件配置的机器集群上。Spark 中的一个核心抽象是弹性分布式数据集(RDD)。RDD 是由 Spark 应用创建的(在Spark Driver上),由集群管理,如下图。
Spark 提供一个弹性分布式数据集,可以认为它是一个分布式的常驻内存的数组。
组成 RDD 分布式数据集的数据分区会被加载到集群的机器上。
基于内存的数据处理
Spark 执行的大部分操作都是在随机访问内存中(RAM)进行。Spark 是基于内 存的,而 Hadoop Map/Reduce 是顺序处理数据,所以 Spark 比 Hadoop 更适合处理 随机访问的图数据。
Spark 的关键好处在于交互式查询和迭代处理过程中在内存中缓存 RDD。缓存 起来的 RDD 可以避免每次重新处理父 RDD 链,而只需要直接返回父 RDD 计算后 的缓存结果。
自然的,这意味着要用到 Spark 的基于内存的计算处理特性,要求集群中的机 器内存要足够大。要是可用内存不够,那么 Spark 就会优雅地溢出数据到磁盘,以 保证 Spark 能继续运行。
当然 Spark 集群也需要一个持久化存储数据的地方,而且还要是分布式存储系 统才行,可选的有 HDFS、Cassandra 和亚马逊的 S3。