gitbook/大规模数据处理实战/docs/100902.md

94 lines
9.3 KiB
Markdown
Raw Permalink Normal View History

2022-09-03 22:05:03 +08:00
# FAQ第二期 | Spark案例实战答疑
你好,我是蔡元楠。
这里是第二期答疑上周我们结束了Spark部分的内容时隔一周我们的Spark案例实战答疑终于上线了。
通过10讲的Spark学习相信你已经对Spark的基本特性有了深入的了解也基本掌握了如何使用各类常用API如RDD、DataSet/DataFrame、Spark Streaming和Structured Streaming。今天我将针对模块三中提出的一些共性留言做一个集中答疑。
我首先要为积极留言的同学们点个赞感谢同学们亲自动手实践有的同学还通过查阅官方API文档的形式找出了正确的实现方式这非常值得鼓励。
## 第18讲
![](https://static001.geekbang.org/resource/image/87/81/871a0e71b4bc152a33bc4429b1ce1881.jpg)
在第18讲中kylin同学留言问到为什么用我们通篇用的是DataFrame API而不是DataSet。这是因为PySpark的SQL库只有DataFrame并没有DataSet。不过在Scala和Java中DataSet已经成为了统一的SQL入口。
![](https://static001.geekbang.org/resource/image/f0/2d/f02f83eb7122e419a80467465d7f4f2d.jpg)
斯盖丸同学问道第18讲代码中groupBy(value)中value是什么意思
这里我说一下SparkSession.read.text()读取文件后生成的DataFrame只有一列它的默认名字就是“value”。
在[第18讲](https://time.geekbang.org/column/article/97658)的代码中我们用lines.value去读取这一列是同样的道理。之后我们给新的列重命名为”word”所以groupBy的参数变成了”word”。如果你印象不深了可以返回去查看一下。
讲到这里我要为Jerry同学点个赞。在开篇词中我就提到过我希望你可以建立自己的批判性思维不要盲目听从任何人的意见。除了认真实践像Jerry一样通过查阅官方文档找到了正确的实现方式做的真的很棒希望可以在工作中也把这份批判性思维和独立学习的能力保持下去。
你可以发现在第18讲中我介绍的explode和split方法在官方文档中都有详细的讲解这些内容并没有很大的难点通过自己阅读官方文档都可以学会。官方文档中还有很多我没有提到的用法在仅仅10讲的篇幅中我不能把Spark的每一个用法都教给你。我能做的只是从源头出发分析新的技术、新的API产生的原因教会你思考的方式并结合例子让你体会一下如何在真实场景中利用这些技术而不是照本宣科地把官方文档复述一遍。
学习新的技术跟上学时背单词不一样,我们要做的是在最短时间内掌握核心内容和设计的理念,至于具体的用法,完全可以在用到的时候查阅官方文档。
## 第19讲
![](https://static001.geekbang.org/resource/image/1c/c8/1c0d97ad6c8f972e73d5f8b4f42226c8.jpg)
[第19讲](https://time.geekbang.org/column/article/98374)中gotojeff提出的这个语言选择问题其实我之前就提到过PySpark现在不支持DataSet只有Scala和Java支持。这是由语言特性决定的Python是动态类型的语言而DataSet是强类型的要求在编译时检测类型安全。所以在所有用Python的代码例子中我用的都是DataFrame。
大部分人都同意在Spark中Scala和Python是优于Java和R的。至于在Spark生态中Scala和Python孰优孰劣这是个很主观的问题我们不能只因为不支持DataSet这一点就说Python比Scala差。
Scala确实很优秀Spark原生的实现就是用Scala写的所以任何新发布的功能肯定支持Scala官方文档也都是用Scala来举例子。而且Scala的性能要优于Python。但是Python也有很多优点比如容易学习、应用场景广。这两种语言在Spark的世界中都可以满足我们绝大多数的需求选择任何一个都不是错误的。
## 第20讲
[第20讲](https://time.geekbang.org/column/article/98537)的思考题是为什么流的Inner-Join不支持完全输出模式对于Inner-Join而言加水印是否是必须的 Outer-Join呢
![](https://static001.geekbang.org/resource/image/fa/a1/fa0bb8291f2c598c132bcd25322691a1.jpg)
![](https://static001.geekbang.org/resource/image/bf/86/bfcf59d05ac93f3a7583a03221abb786.jpg)
![](https://static001.geekbang.org/resource/image/2a/19/2a5716bc940cf44729642af5e627f219.jpg)
这里我节选了never leave、jon、Ming的留言里面是他们对这个思考题的回答不知道你是不是也进行了深入的思考那么现在让我也来分享一下我的看法吧。
首先现阶段不仅Inner-join不支持完全输出模式任何类型的Join都不支持完全输出模式。
这是因为完全输出模式要求每当有新数据输入时输出完整的结果表。而对于无边界数据我们很难把所有历史数据存在内存中。所以一般Join的都是在某个时间窗口内的流数据这就是引入watermarking的原因。希望将来Spark可以引入新的机制来支持这一点。
其次我们都知道Outer join是要在Inner Join的基础上把没有匹配的行的对应列设为NULL。但是由于流数据的无边界性Spark永远无法知道在未来会不会找到匹配的数据。所以为了保证Outer Join的正确性加水印是必须的。这样Spark的执行引擎只要在水印的有效期内没找到与之匹配的数据就可以把对应的列设为NULL并输出。
那么Inner Join呢由于Inner Join不需要连接两个表中所有的行所以在Spark官网的叙述中水印和事件时间的限制不是必须的。但是如果不加任何限制流数据会不断被读入内存这样无疑是不安全的。所以我推荐你即便是Inner Join也要加水印和事件时间的限制。
![](https://static001.geekbang.org/resource/image/d9/90/d99e079892a952534d9214628f3a9590.jpg)
Feng.X同学不是很理解实例中两个Streaming DataFrame Join时为什么要加上事件时间的限制“endTime > startTime AND endTime <= startTime + interval 2 hours”。
事实上这个限制会抛弃任何长于2个小时的出租车出行数据。确实对于这个例子来说这样一个对事件时间的限制并不是必须的。加入它其实是为了告诉你在基于事件时间来join两个流时我们一般不考虑时间跨度过大的情况因为它们没有普遍意义还会影响数据分析的结果。
举个例子吧,对于一个网页广告来说,我们需要知道用户看到一个广告后要多长时间才会去点击它,从而评估广告的效果。
这里显然有两个流一个代表用户看到广告的事件另一个代表用户点击广告的事件。尽管我们可以通过用户的ID来Join这两个流但是我们需要加一个限制就是点击广告的时间不能比看到广告的时间晚太久否则Join的结果很可能是不准确的。比如用户可能在1:00和2:00都看到了广告但是只在2:01点击了它我们应该把2:00和2:01Join起来而不应该Join1:00和2:01因为1:00看到的广告并没有促使他点击。
## 第21讲
[第21讲](https://time.geekbang.org/column/article/99152)的思考题是除了高延迟的流处理这一缺点外你认为Spark还有什么不足可以怎样改进
我们都知道Spark并不是一个完美的数据处理框架它的优点明显也同样有不少不足之处。
* 在数据规模很大的场景中,靠内存处理的开销很大。如果内存不够把中间结果写入硬盘的话,又会影响处理速度;
* Spark没有自己的文件管理系统它对HDFS或者其他的文件系统依赖很强
* 在流处理中,只支持基于时间的窗口,而不支持其他种类的窗口,比如基于数据个数的窗口。
正是由于Spark还有诸多不足所以众多开源的贡献者才会持续对Spark做改进Spark也在不断发布新版本。此外很多新的数据处理框架的发明也是为了从根本上解决Spark存在的问题比如Flink还有我们正在学习的Apache Beam。
![](https://static001.geekbang.org/resource/image/ac/33/acedc4e52bfe3ed31bff3afae3e0b633.jpg)
这位飞哥grapefruit不太明白Flink支持基于数据本身窗口是什么意思我来回答一下。
窗口是流数据处理中最重要的概念之一窗口定义了如何把无边界数据划分为一个个有限的数据集。基于事件时间的窗口只是窗口的一种它是按照事件时间的先后顺序来划分数据比如说1:00-1:10是一个集合1:10-1:20又是一个集合。
但是窗口并不都是基于时间的。比如说我们可以按数据的个数来划分每接受到10个数据就是一个集合这就是Count-based Window基于数量的窗口。Flink对于窗口的支持远比Spark要好这是它相比Spark最大的优点之一。它不仅支持基于时间的窗口处理时间、事件时间和摄入时间还支持基于数据数量的窗口。
此外在窗口的形式上Flink支持滚动窗口Tumbling Window、滑动窗口Sliding Window、全局窗口Global Window和会话窗口Session Windows
到这里我们Spark案例实战篇的答疑就结束了。欢迎继续留言与我分享你的问题与答案。如果你觉得有所收获也欢迎把文章分享给你的朋友。