You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
9.8 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 12 | 我们并没有觉得MapReduce速度慢直到Spark出现
Hadoop MapReduce虽然已经可以满足大数据的应用场景但是其执行速度和编程复杂度并不让人们满意。于是UC Berkeley的AMP Lab推出的Spark应运而生Spark拥有更快的执行速度和更友好的编程接口在推出后短短两年就迅速抢占MapReduce的市场份额成为主流的大数据计算框架。
读到这里请你先停一下,请给这段看似“没毛病”的引子找找问题。
不知道你意识到没有我在这段开头说的“Hadoop MapReduce虽然已经可以满足大数据的应用场景但是其执行速度和编程复杂度并不让人们满意”这句话其实是错误的。这样说好像可以让你更加清晰地看到事物发展的因果关系同时也可以暗示别人自己有洞察事物发展规律的能力。然而这种靠事后分析的因果规律常常是错误的**往往把结果当作了原因**。
事实上在Spark出现之前我们并没有对MapReduce的执行速度不满我们觉得大数据嘛、分布式计算嘛这样的速度也还可以啦。至于编程复杂度也是一样一方面Hive、Mahout这些工具将常用的MapReduce编程封装起来了另一方面MapReduce已经将分布式编程极大地简化了当时人们并没有太多不满。
真实的情况是人们在Spark出现之后才开始对MapReduce不满。原来大数据计算速度可以快这么多编程也可以更简单。而且Spark支持Yarn和HDFS公司迁移到Spark上的成本很小于是很快越来越多的公司用Spark代替MapReduce。也就是说因为有了Spark才对MapReduce不满而不是对MapReduce不满所以诞生了Spark。真实的因果关系是相反的。
这里有一条关于问题的定律分享给你:**我们常常意识不到问题的存在,直到有人解决了这些问题。**
当你去询问人们有什么问题需要解决,有什么需求需要被满足的时候,他们往往自己也不知道自己想要什么,常常言不由衷。但是如果你真正解决了他们的问题,他们就会恍然大悟:啊,这才是我真正想要的,以前那些统统都是“垃圾”,我早就想要这样的东西(功能)了。
所以顶尖的产品大师(问题解决专家),并不会拿着个小本本四处去做需求调研,问人们想要什么。而是在旁边默默观察人们是如何使用产品(解决问题)的,然后思考更好的产品体验(解决问题的办法)是什么。最后当他拿出新的产品设计(解决方案)的时候,人们就会视他为知己:你最懂我的需求(我最懂你的设计)。
乔布斯是这样的大师Spark的作者马铁也是这样的专家。
说了那么多我们回到Spark。Spark和MapReduce相比有更快的执行速度。下图是Spark和MapReduce进行逻辑回归机器学习的性能比较Spark比MapReduce快100多倍。
![](https://static001.geekbang.org/resource/image/8d/82/8ddcf04a70141da96e83cb07b8969c82.png)
除了速度更快Spark和MapReduce相比还有更简单易用的编程模型。使用Scala语言在Spark上编写WordCount程序主要代码只需要三行。
```
val textFile = sc.textFile("hdfs://...")
val counts = textFile.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://...")
```
不熟悉Scala语言没关系我来解释一下上面的代码。
第1行代码根据HDFS路径生成一个输入数据RDD。
第2行代码在输入数据RDD上执行3个操作得到一个新的RDD。
* 将输入数据的每一行文本用空格拆分成单词。
* 将每个单词进行转换,`word => (word, 1)`,生成<Key, Value>的结构。
* 相同的Key进行统计统计方式是对Value求和`(_ + _)`。
第3行代码将这个RDD保存到HDFS。
RDD是Spark的核心概念是弹性数据集Resilient Distributed Datasets的缩写。RDD既是Spark面向开发者的编程模型又是Spark自身架构的核心元素。
我们先来看看作为Spark编程模型的RDD。我们知道大数据计算就是在大规模的数据集上进行一系列的数据计算处理。MapReduce针对输入数据将计算过程分为两个阶段一个Map阶段一个Reduce阶段可以理解成是**面向过程的大数据计算**。我们在用MapReduce编程的时候思考的是如何将计算逻辑用Map和Reduce两个阶段实现map和reduce函数的输入和输出是什么这也是我们在学习MapReduce编程的时候一再强调的。
而Spark则直接针对数据进行编程将大规模数据集合抽象成一个RDD对象然后在这个RDD上进行各种计算处理得到一个新的RDD继续计算处理直到得到最后的结果数据。所以Spark可以理解成是**面向对象的大数据计算**。我们在进行Spark编程的时候思考的是一个RDD对象需要经过什么样的操作转换成另一个RDD对象思考的重心和落脚点都在RDD上。
所以在上面WordCount的代码示例里第2行代码实际上进行了3次RDD转换每次转换都得到一个新的RDD因为新的RDD可以继续调用RDD的转换函数所以连续写成一行代码。事实上可以分成3行。
```
val rdd1 = textFile.flatMap(line => line.split(" "))
val rdd2 = rdd1.map(word => (word, 1))
val rdd3 = rdd2.reduceByKey(_ + _)
```
RDD上定义的函数分两种一种是转换transformation函数这种函数的返回值还是RDD另一种是执行action函数这种函数不再返回RDD。
RDD定义了很多转换操作函数比如有计算**map**(func)、过滤**filter**(func)、合并数据集**union**(otherDataset)、根据Key聚合**reduceByKey**(func, \[numPartitions\])、连接数据集**join**(otherDataset, \[numPartitions\])、分组**groupByKey**(\[numPartitions\])等十几个函数。
我们再来看看作为Spark架构核心元素的RDD。跟MapReduce一样Spark也是对大数据进行分片计算Spark分布式计算的数据分片、任务调度都是以RDD为单位展开的每个RDD分片都会分配到一个执行进程去处理。
RDD上的转换操作又分成两种一种转换操作产生的RDD不会出现新的分片比如map、filter等也就是说一个RDD数据分片经过map或者filter转换操作后结果还在当前分片。就像你用map函数对每个数据加1得到的还是这样一组数据只是值不同。实际上Spark并不是按照代码写的操作顺序去生成RDD比如`rdd2 = rdd1.map(func)`这样的代码并不会在物理上生成一个新的RDD。物理上Spark只有在产生新的RDD分片时候才会真的生成一个RDDSpark的这种特性也被称作**惰性计算**。
另一种转换操作产生的RDD则会产生新的分片比如`reduceByKey`来自不同分片的相同Key必须聚合在一起进行操作这样就会产生新的RDD分片。实际执行过程中是否会产生新的RDD分片并不是根据转换函数名就能判断出来的具体我们下一期再讨论。
总之你需要记住Spark应用程序代码中的RDD和Spark执行过程中生成的物理RDD不是一一对应的RDD在Spark里面是一个非常灵活的概念同时又非常重要需要认真理解。
当然Spark也有自己的生态体系以Spark为基础有支持SQL语句的Spark SQL有支持流计算的Spark Streaming有支持机器学习的MLlib还有支持图计算的GraphX。利用这些产品Spark技术栈支撑起大数据分析、大数据机器学习等各种大数据应用场景。
![](https://static001.geekbang.org/resource/image/38/0f/3894be10797c657af3a54bc278ab780f.png)
我前面提到,顶尖的产品设计大师和问题解决专家,不会去询问人们想要什么,而是分析和观察人们的做事方式,从而思考到更好的产品设计和问题解决方案。
但是这种技巧需要深邃的观察力和洞察力,如果没有深度的思考,做出的东西就会沦为异想天开和自以为是。要知道大众提出的需求虽然也无法触及问题的核心,但是好歹是有共识的,大家都能接受,按这种需求做出的东西虽然平庸,但是不至于令人厌恶。
而缺乏洞见的自以为是则会违反常识,让其他人本能产生排斥感,进而产生对立情绪。这种情绪之下,设计没有了进一步改进的基础,最后往往成为悲剧。这两年在所谓互联网思维的鼓吹下,一些缺乏专业技能的人,天马行空创造需求,受到质疑后公开批评用户,也是让人倍感惊诧。
我们在自己的工作中,作为一个不是顶尖大师的产品经理或工程师,如何做到既不自以为是,又能逐渐摆脱平庸,进而慢慢向大师的方向靠近呢?
有个技巧可以在工作中慢慢练习:**不要直接提出你的问题和方案**,不要直接说“你的需求是什么?”“我这里有个方案你看一下”。
直向曲中求,对于复杂的问题,越是直截了当越是得不到答案。迂回曲折地提出问题,一起思考问题背后的规律,才能逐渐发现问题的本质。通过这种方式,既能达成共识,不会有违常识,又可能产生洞见,使产品和方案呈现闪光点。
* 你觉得前一个版本最有意思(最有价值)的功能是什么?
* 你觉得我们这个版本应该优先关注哪个方面?
* 你觉得为什么有些用户在下单以后没有支付?
## 思考题
你在工作、生活中通过提问发现问题背后的本质、现象背后的规律的例子有哪些?或者你观察到同事、朋友这样的例子有哪些?
欢迎你写下自己的思考或疑问,与我和其他同学一起讨论。