赞
踩
你好,很高兴我们在《即学即用的 Spark 实战 44 讲》这个课程中相遇,我是范东来,Spark Contributor 和 Superset Contributor,同样也是《Spark 海量数据处理》与《Hadoop 海量数据处理》两本书的作者。
谈起大数据技术的学习,我觉得自己很幸运,研究生阶段就通过实验室项目积累了很多实践经验,毕业后在担任技术负责人和架构师的过程中,主导参与过国内很多金融机构的大数据项目与平台实施,搭建过整个公司的大数据架构和平台,积累了很多大数据技术的心得。我平时喜欢总结学习,也喜欢分享探讨,在这里我将这些经验梳理汇总,希望也能助你一臂之力。
掌握利器,跟上时代的步伐
如今,数据的增长速度以及重要意义已经无需多言,互联网企业对于数据的利用效率,在某种程度上决定了其企业竞争力,而数据处理技术很大程度上就决定了数据的利用效率。
诞生于 2009 年的 Apache Spark,目前已成为全球范围内最流行、功能最全面、社区最活跃的大数据处理技术。从 GitHub 的数据中可以看到,在 Apache 的所有开源项目中,Spark 的关注度排名第 3(前两位分别是 RPC 服务框架 Dubbo 和可视化平台 Superset),在所有大数据处理技术中排名第 1。
此外,Spark 在资本市场也得到了极高的认可,其背后的商业化公司得到了 62 亿美元的估值。目前,绝大多数公司和组织会基于 Spark 生态搭建自己的大数据平台,构建支持业务的数据管道。可以说,提到大数据处理,Spark 已是一个无法回避的话题。
Spark 之于大数据工程师,就好像 Java 之于后端工程师:学会了并不能保证你一定能够拿到 Offer,但是不会,拿到 Offer 的可能性很小。
另外,Spark 也很适合数据科学家与数据分析师进行中小规模数据处理,多语言接口与 SQL 支持让它赢得了很多分析师用户,而且这部分用户中 Spark 使用者的占比也越来越大,俨然成为了数据工程与数据科学的通用方案。
既然我们在这个专栏里相遇,你应该也已经跃跃欲试,想要掌握 Spark 了。不过别急,在开始学习 Spark 之前,先来看看下面几个问题,它们能更好地帮你理解这门课程。
Spark 适合谁学?
关于 Spark 的定义,Spark官方给出的说法是:一个通用的快速分析引擎。
这里的“通用”有很多含义,我不展开讲,你可以简单地把它理解为:“供所有的大数据从业人员使用”。客观地说,这还是一种愿景,不过随着 Spark 3.0 的预发布,距离实现已经很近了。
“分析”,表示 Spark 主要面向的是数据处理场景。这里用“分析”这个词,其实是一种委婉的说法,希望那些有分析需求的轻度用户也能注意到 Spark,如果用“分布式计算框架”这类字眼,虽然听起来更为专业,但或许会让一部分人望而生畏。
从业以来,我在不断自我成长与角色转换的过程中,目睹了公司层面大数据架构的演进以及对人才需求的变化。
严格意义上说,如果一个组织的大数据架构基础越薄弱,对于大数据工程师的需求就会越大,但是当这个组织的大数据架构愈加趋于完善与成熟,对于数据分析师的需求就会变大。而这两类职位涵盖了数据工程与数据科学领域,都需要掌握 Spark。
而行业层面,大数据人才将长期保持供不应求的状态。也因此,大数据工程师与数据分析师的行业薪资相对更高,往往成为应用开发工程师可选的职业发展目标。下面是拉勾平台的大厂招聘信息,你可以看到薪资的差距:
我见过一位分析师,他平时需要分析样本数据,虽然处理的数据量并不大,但有时仍会选用 Spark,而非 pandas 等这类传统的数据分析软件。我最开始也有些好奇,问其原因,他说是因为 Spark 接口简单还支持 SQL,这对于他处理数据非常方便。虽然是个例,但这也恰好说明 Spark 的易用性。这一优点,不仅使之成为工程师等人群的必用工具,更得到了数据分析师群体的认可。
简言之,Spark 为我们常见的批处理、流处理、数据分析、数据探索、机器学习等场景都提供了很好的解决方案,任何有数据处理需求的人,都可以用它来完成自己的研究与日常工作。
关于 Spark 适合谁学,你不妨从 Spark 的一些使用场景来对照体会一下。
如果你是一名数据分析爱好者?
你可能有一些编程基础,对数字敏感,平时喜欢从公开数据中发现一些有趣的故事,你处理的数据量一般都不大,平时用 pandas、NumPy 也能很好地完成数据处理需求,但是因为听说 Spark 对 SQL 支持很好,很方便,现在想试试 Spark。
如果你是一名分析师?
你是大数据公司的一名量化分析师,对 R、Python 很熟悉,公司一般会有统一的大数据平台,数据和计算资源都对你开放,作为平台的用户,你的工作就是根据业务需求对全量数据进行探索、处理、分析和建模。你们处理的数据量有时会非常大,正好大数据平台支持 Spark,并且 Spark 也提供了 R 和 Python 编程接口,于是想试试 Spark,看能不能提升工作效率。
如果你是一名大数据工程师?
你原来是 Java 程序员,现在公司已经有了功能齐全的大数据平台,你们需要根据业务需求开发离线计算的批处理应用,还有实时计算的流处理应用,现在最成熟的工具就是 Spark,可以满足所有需求。
如果你是一名大数据架构师?
你经验丰富,需要为公司搭建一整套大数据平台,设计出支撑业务的数据管道,以满足公司内部分析师和工程师的使用需求,目前 Spark 生态可以很好地满足公司不同层次的数据处理需求,如离线计算、实时处理、数据挖掘等。
都说 Spark 难学,问题究竟出在哪儿?
工作中,我发现很多人对 Spark 有一种天然的“距离感”,总是说“太难了”“更新太快了”,主要原因无外乎:
看 Spark 的官方文档,有很多新概念很抽象,例如弹性分布式数据集等;此外,Spark 在 2.0 的时候全面更新了一次,与之前的老版本差异很大。
Spark 是一个分布式系统,与以前熟悉的技术有着本质的不同。
因为动手能力较差,无法搭建(或者很难搭建)可以运行的 Spark 环境。
虽然 Spark 图书不少(包括我自己写的《Spark 海量数据处理》),但基本上都是从原理出发,内容事无巨细,大而全,无形中拉升了学习成本。
而且,跟着书中的讲述结构进行学习,还容易出现三个问题:
书上的代码跑不通或者看不懂,无法积累实践经验。
在不重要且复杂的细节上耗费太多时间,不能针对当下所需高效学习,快速提升业务能力;
学习不高效,时间付出不对等,与实践欠缺导致的理解不够深刻,则更加抓不住重点,都容易让你陷入恶性循环。
这也是我开设这个专栏的初衷,希望给你一个实践与理论并重,同时又能够帮你抓住关键问题,快速学习提高的 Spark 课程,让你在碎片时间就能有效学习。
我是如何设计这门课程的?
每个人的学习能力是不同的,学习 Spark 也一样,有的人上手很快,有的人却苦苦徘徊于门外。
在这个专栏里,我特别针对不同背景和需要的用户做了优化,以便为你提供一个平缓的 Spark 学习曲线,这个课程:
简洁、实用:将复杂而不重要的细节去掉,仅保留必学的主干内容。
突出实践,人人都能学,学了就能用。除了一开始的 2 个模块,后面每个模块后都会有 1 个到 2 个实践案例来结尾。
一个完整的实战项目,带你串联和巩固所学知识。专栏最后专门有一个“商业智能系统实战”模块,在这个模块里,带你用 Spark 完整地体验一个商业智能系统的开发流程。
基于此,本专栏分为 7 个模块,对于不同的读者类型,侧重点有所不同。对于数据分析爱好者和分析师这类用户来说,模块 2 需要了解,模块 4 可以忽略,其余需要掌握;对于大数据工程师来说,除了模块 6 可以根据需要进行学习,其余都需要掌握;对于大数据架构师来说,所有都必须掌握。
下面是专栏的目录图,可以看到除了 Spark 基础知识,本课程还涵盖了当下流行的流处理、图挖掘、机器学习等内容,掌握这些必定可以让你在激烈的人才竞争中脱颖而出斩获心仪Offer,最后再以一个实践项目作为结尾,相信你能获得对 Spark 理性和感性的认知。
写在最后
无论大数据工程师,还是数据分析师,你都需要了解技术和业务,不过比重有些不同。工程师对业务理解到了一定程度,要想在技术深度和广度上有所建树,成长为架构师,那么阅读源码并形成自己的思考一定会大有裨益。当然,如果你能向 Spark 贡献代码并被接受,那就更好了,所以我在课程最后增加了篇“如何阅读 Spark 源码并向 Spark 贡献代码”的文章。而对于数据分析师,技术只是工具,结合业务探索数据本身蕴含的价值才是重点。
点亮了 Spark 的技能,就像魔兽世界的满级,游戏才刚刚开始,希望你永远保持对技术、对数据的好奇心。在我当初开始大数据生涯时,投入了大量精力翻阅和学习各种资料,然后筛选和实践踩坑,那时我就在想,如果有一个能够帮我“先做减法再做加法”的专栏,该多好。很高兴的是,现在我可以来做这件事,在拉勾教育这个平台与你分享。
准备好了吗?让我们摩拳擦掌,一起开始这个学习与探讨的过程。为了更好地给你提供帮助,我也非常希望听到你的反馈,欢迎你在留言区给我留言,你也可以在这里和不同的用户交流经验,共同成长。
你好,我是你的 Spark 老师范东来,今天是本课程基础模块的第一节课,我们来聊聊一个比较基础也比较重要的内容 MapReduce,说它基础,是因为它诞生的时间实在是太久远了,并不是什么新东西,说它重要则是因为基于它的提出衍生出很多重要的技术,比如我们关心的 Spark。
今天的内容主要有以下几点:
Google 的三驾马车;
MapReduce 编程模型与 MapReduce 计算框架;
并发与并行;
如何理解分布式计算框架的编程接口与背后的工程实现。
Google 的三驾马车
USNew 把计算机科学分为 4 个领域:人工智能、编程语言、系统以及理论。其中的系统领域有两大顶级会议,一个是 ODSI(USENIX conference on Operating Systems Design and Implementation),另一个是 SOSP(ACM Symposium on Operating Systems Principles),这两个会议在业界的分量非常重,如果把近几十年关于这两个会议的重要论文收录到一本书,就可以看作是操作系统和分布式系统的一本教科书。
从 2003 年到 2006 年,Google 分别在 ODSI 与 SOSP 发表了 3 篇论文,引起了业界对于分布式系统的广泛讨论,这三篇论文分别是:
SOSP2003:The Google File System;
ODSI2004:MapReduce: Simplifed Data Processing on Large Clusters;
ODSI2006:Bigtable: A Distributed Storage System for Structured Data。
在 2006 年,Google 首席执行官施密特提出了云计算这个词语,Google 的这 3 篇论文也被称为 Google 的三驾马车,代表 Google 大数据处理的基石、云计算的基础。不过值得注意的是,虽然 Google 作为业界领军者经常会将自己的技术开源出来,但是客观地讲,Google 开源出来的技术并不是内部使用的最新技术,中间甚至会有代差,这也侧面反映出 Google 的技术实力。
第 1 篇论文主要讨论分布式文件系统,第 2 篇论文主要讨论的分布式计算框架,第 3 篇论文则主要讨论分布式数据存储。这 3 篇论文揭开了分布式系统神秘的面纱,为大数据处理技术做出了重要的贡献。 有了这 3 篇论文的理论基础与后续的一系列文章,再加上开源社区强大的实践能力,Hadoop、HBase、Spark 等很快走上了台前,大数据技术开始呈现出一个百花齐放的状态。
MapReduce 编程模型与 MapReduce 计算框架
在发表的第 2 篇文章中,Google 很明确地表示 MapReduce 是其实现的一个分布式计算框架,其编程模型名为 MapReduce。开源社区基于这篇论文的内容,照猫画虎地实现了一个分布式计算框架,也叫作 MapReduce。但一些书籍和网上的资料在提到 MapReduce 的时候并未说明,容易造成困惑。其实 Google 拿编程模型的名字直接作为计算框架的名字这种例子还有很多,比如 Google Dataflow。而 MapReduce 有两个含义,一般来说,在说到计算框架时,我们指的是开源社区的 MapReduce 计算框架,但随着新一代计算框架如 Spark、Flink 的崛起,开源社区的 MapReduce 计算框架在生产环境中使用得越来越少,逐渐退出舞台。
MapReduce 的第二个含义是一种编程模型,这种编程模型来源于古老的函数式编程思想,在 Lisp 等比较老的语言中也有相应的实现,并随着计算机 CPU 单核性能以及核心数量的飞速提升在分布式计算中焕发出新的生机。
MapReduce 模型将数据处理方式抽象为 map 和 reduce,其中 map 也叫映射,顾名思义,它表现的是数据的一对一映射,通常完成数据转换的工作,如下图所示:
reduce 被称为归约,它表示另外一种映射方式,通常完成聚合的工作,如下图所示:
圆角框可以看成是一个集合,里面的方框可以看成某条要处理的数据,箭头表示映射的方式和要执行的自定义函数,运用 MapReduce 编程思想,我们可以实现以下内容:
将数据集(输入数据)抽象成集合;
将数据处理过程用 map 与 reduce 进行表示;
在自定义函数中实现自己的逻辑。
这样就可以实现从输入数据到结果数据的处理流程(映射)了。
并发与并行
一般来说,底层的东西越简单,那么上层的东西变化就越复杂,对于 MapReduce 编程模型来说,map 与 reduce 的组合加上用户定义函数,对于业务的表现力是非常强的。这里举一个分组聚合的例子,如下图所示:
map 端的用户自定义函数与 map 算子对原始数据人名进行了转换,生成了组标签:性别,reduce 端的自定义函数与 reduce 算子对数据按照标签进行了聚合(汇总)。
MapReduce 认为,再复杂的数据处理流程也无非是这两种映射方式的组合,例如 map + map + reduce,或者 reduce 后面接 map,等等,在我展示出的这张图里你可以看到相对复杂的一种组合形式:
很多支持函数式编程的语言,对于语言本身自带的集合数据结构,都会提供 map、reduce 算子。现在,我们可以很容易的将第一个圆角方框想象成一个数十条数据的集合,它是内存中的集合变量,那么要实现上图中的变换,对于计算机来说,难度并不大,就算数据量再大些,我们也可以考虑将不同方框和计算流程交给同一台计算机的 CPU 不同的核心进行计算,这就是我们说的并行和并发。
如何理解分布式计算框架的编程接口与背后的工程实现
现在你可以想象下,随着数据集继续增大,要处理的数据(上图中开始的集合)超过了计算内存的大小,那么就算是逻辑非常简单的流程,也要考虑中间结果的存储。比如计算过程涉及到硬盘和内存之前的数据交换等等之类的工程实现的问题,虽然在这个过程中上面 3 步并没有发生变化,但是背后实现的系统复杂度大大提高了。
我们可以再发挥想象,将上图中的圆角框想象成一个极其巨大的数据集,而方框想象成大数据集的一部分,我们会发现,对于从输入数据到结果数据的映射需求来说,前面 3 步仍然适用,只是这个集合变得非常大。
但是由于数据量的急剧扩大,相比于刚才的第 2 种情况,背后工程实现的复杂度会成倍增加,当整个数据集的容量和计算量达到 1 台计算机能处理的极限的时候,我们就会想办法把图中方框所代表的数据集分别交给不同的计算机来完成,那么如何调度计算机,如何实现 reduce 过程中不同计算机之间的数据传输等问题,就是 Spark 基于 MapReduce 编程模型的分布式实现,这也是我们常常所说的分布式计算。
从上图可以看出,在 reduce 过程中,会涉及到数据在不同计算机之间进行传输,这也是 MapReduce 模型下的分布式实现的一个关键点,后面我们会讲到 Spark 是如何做的。
看到这里,你可能对分布式运算有一个感性的认识,以小见大,函数式语言本身就提供了类似于 map、reduce 的操作,如下图第 1、2 行代码:
1、2 行是函数式编程语言 Scala 对于集合的处理,3、4 行是 Spark 对集合的处理,逻辑同样是对集合元素都加 1 再过滤掉小于等于 1 的元素并求和。对于 Spark 来说,处理几十 GB到几十 TB 的数据集,第2行代码或者说第4行代码同样适用,只是 list 变得比较特殊,它不是只存在于一台计算机的内存里,而是存在于多台计算机的磁盘和内存上。
现在,我们可以这样理解基于 MapReduce 编程模型的分布式计算框架,其编程接口与普通函数式语言的数据处理并没有什么不同(甚至可以说完全一样),但是背后的工程实现千差万别,而像 Spark、MapReduce 这样的框架,它们的目标都是尽力为用户提供尽可能简单的编程接口以及高效地工程实践。从这个角度上来讲,我们可以把 Spark 看成是一种分布式计算编程语言,它的终极目标是希望达到这样一种体验:让用户处理海量数据集时与处理内存中的集合变量并没有什么不同。
MapReduce 这种思想或者编程模型已经出现几十年了,不变的是思想,变得是使用场景和实现方法。我相信未来一定会有效率优于 Spark 的计算框架出现,就像 Spark 优于普通的编程语言一样。
总结
本课时的主要目的是在深入讲解 Spark 之前,对 Spark 之前的技术、范式、抽象进行一个简单的讲解,为后面的学习打下基础。
在上个课时中我们提到,Google 在 2004~2006 年发表了被称为 Google 三驾马车的 3 篇论文,这在开源社区可谓是一石激起千层浪。很快,基于论文的开源实现就问世了,其中第 1 篇论文的 GFS 和第 2 篇论文的 MapReduce 开源实现为 HDFS 与 MapReduce,统称为 Hadoop,第 3 篇 Bigtable 论文开源实现为 HBase,本课时不展开讨论。
Hadoop 的出现,对于坐拥数据而苦于无法分析的用户来说,无疑是久旱逢甘霖,加之那段时间移动互联网的流行,数据呈几何倍数增长,Hadoop 在很大程度上解决了数据处理的痛点。在很长的一段时间里,Hadoop 是大数据处理的事实标准,直到现在,很多公司的大数据处理架构也是围绕 Hadoop 而建的。
基于此,本课时主要讨论以下几个问题:
Hadoop 1.0
Hadoop 2.0
Hadoop 生态圈与发行版
Hadoop 大数据平台
Hadoop 的趋势
Hadoop 1.0
Hadoop 从问世至今一共经历了 3 个大版本,分别是 1.0、2.0 与最新的 3.0,其中最有代表性的是 1.0 与 2.0,3.0 相比于 2.0 变化不大。Hadoop 1.0 的架构也比较简单,基本就是按照论文中的框架实现,其架构如下图所示:
其中,下层是 GFS 的开源实现 HDFS(Hadoop 分布式文件系统),上层则是分布式计算框架 MapReduce,这样一来,分布式计算框架基于分布式文件系统,看似非常合理。但是,在使用的过程中,这个架构还是会出现不少问题,主要有 3 点:
主节点可靠性差,没有热备;
提交 MapReduce 作业过多的情况下,调度将成为整个分布式计算的瓶颈;
资源利用率低,并且不能支持其他类型的分布式计算框架。
第 1 点是小问题,涉及到对系统可用性方面的改造,但是第 2 点与第 3 点提到的问题就比较犀利了。
第 2 个问题在于,Hadoop 1.0 的分布式计算框架 MapReduce 并没有将资源管理和作业调度这两个组件分开,造成当同时有多个作业提交的时候,资源调度器会不堪重负,导致资源利用率过低;第 3 个问题则是不支持异构的计算框架,这是什么意思呢?其实当时 Spark 已经问世了,但是如果这个集群部署了 Hadoop 1.0,那么想要运行 Spark 作业就必须另外再部署一个集群,这样无疑是对资源的浪费,很不合理,不过这也没办法,因为这属于直接套用论文造成的历史遗留问题。
Hadoop 2.0
基于这些问题,社区开始着手 Hadoop 2.0 的开发,Hadoop 2.0 最大的改动就是引入了资源管理与调度系统 YARN,代替了原有的计算框架,而计算框架则变成了类似于 YARN 的用户,如下图:
YARN 将集群内的所有计算资源抽象成一个资源池,资源池的维度有两个:CPU 和内存。同样是基于 HDFS,我们可以认为 YARN 管理计算资源,HDFS 管理存储资源。上层的计算框架地位也大大降低,变成了 YARN 的一个用户,另外,YARN 采取了双层调度的设计,大大减轻了调度器的负担,我会在后续课程详细讲解,这里不展开讨论。
Hadoop 2.0 基本上改进了 Hadoop 的重大缺陷,此外 YARN 可以兼容多个计算框架,如 Spark、Storm、MapReduce 等,HDFS 也变成了很多系统底层存储,Hadoop 以一种兼收并蓄的态度网罗了一大批大数据开源技术组件,逐渐形成了一个庞大的生态圈,如下图所示(该图只展示了一部分组件)。在当时,如果你要想搭建一个大数据平台,绝对无法绕过 Hadoop。
Hadoop 生态圈与发行版
Hadoop 生态圈的各个组件包含了 Hadoop 的核心组件,如 HDFS、YARN。在计算层也有了更多的选择,如支持 SQL 的 Hive、Impala,以及 Pig、Spark、Storm 等。还有些工具类的组件,比如负责批量数据抽取的 Sqoop,负责流式数据传输的 Flume,负责分布式一致性的 Zookeeper。此外,还有一些运维类组件,例如负责部署的 Ambari、集群监控的 ganglia 等。这些组件看似繁杂,但都是一个生产环境的所必需的。所以在当时,将如此多的组件集成到一个平台,会有很多各式各样的问题。
很快有公司注意到了这个问题中的商机,其中做的最好的是 Cloudera 和 Hortonworks 这两家公司,它们核心产品就是将上述 Hadoop 生态圈中最常用到的开源组件打包为一个 Hadoop 发行版,Clouera 的叫 CDH,Hortonworks 的叫 HDP,这个发行版中的所有组件不会有兼容性等其他莫名其妙的问题,供用户免费使用,当然也为那些技术实力不强的公司准备了收费版。
在 Hadoop 最鼎盛的阶段,几乎所有公司的大数据平台都使用了这两家公司的 Hadoop 发行版,Cloudera 也得到了资本市场的认可,一度估值 50 亿美金,但随着 Hadoop 的没落,Cloudera 在上市后,股价一直缩水,最后与同样是上市公司的 Hortonworks 进行了合并,合并后的股价仅有 20 亿美金。值得一提的是,Hadoop 之父 Doug Cutting 也是是Cloudera 公司的成员。
Hadoop 大数据平台
学习大数据的时候,你可能习惯把 Hadoop 与原有的应用开发那一套进行类比,但会发现没办法完全对应上,例如Hadoop确实能够用来存储数据,那么Hadoop就是数据库了吗?而很多文章在提到Hadoop的时候,有时会用大数据平台、数据仓库、分布式数据库、分布式计算框架等字眼,看似合理,但又不完全正确,让人非常迷惑。
这里,我对 Hadoop 做一个简单的解释。举例来说,在做传统应用开发的时候,我们不会过多的关注磁盘驱动器,这是因为文件系统已经帮我们进行了抽象,我们只需要使用文件系统 API 就可以操作磁盘驱动器。
同样的,我们在开发应用时也无需关注 CPU 的使用时间,操作系统和编程语言已经帮我们做好了抽象和隔离。所以在提到大数据平台的时候,我们要知道它首先是一个分布式系统,换言之底层是由一组计算机构成的,也就是一个集群。所谓大数据平台,相当于把这个集群抽象成一台计算机,而隔离了底层的细节,让用户使用这个平台时,不会感觉到自己在使用一个分布式系统,而像是在使用一台计算机,很轻松地就可以让整个集群为他所用。为了加深印象,我们可以来对比下两条命令:
hadoop dfs -ls / ls /
条命令是浏览 Hadoop 文件系统(HDFS)的根目录,第二条命令是浏览 Linux 本地文件系统的根目录,如果不进行说明的话,无法看出第一条命令基于分布式文件系统,此外,这么对比的话,可以看到基于集群,Hadoop 为用户提供了一套类似 Liunx 的环境。
因此,Hadoop 可以理解为是一个计算机集群的操作系统,而 Spark、MapReduce 只是这个操作系统支持的编程语言而已,HDFS 是基于所有计算机文件系统之上的文件系统抽象。同理,YARN 是基于所有计算机资源管理与调度系统之上的资源管理与调度系统抽象,Hadoop 是基于所有计算机的操作系统之上的操作系统抽象。所以如果你一定要进行比较的话,Hadoop 应该和操作系统相比较。
Hadoop 的趋势
在 Hadoop 2.0 时期,Hadoop 的存在感还是非常强的,但是就像普通计算机一样,编程语言的热度始终要大于操作系统。随着计算框架的百花齐放,一些新的资源管理与调度系统问世,例如 Kubernets 和 Mesos,Hadoop 的存在感越来越低,在大数据平台中越来越底层,有些大数据平台甚至只采用 HDFS,其余都按照需求选取其他技术组件。
此外,一些计算框架本身就自带生态,如 Spark 的 BDAS,这就逐渐造成了一种现象:Hadoop 的热度越来越低,而分布式计算框架的热度越来越高,就像 Java 的热度肯定比 Linux 高,这也符合计算机的发展规律。
在现在的环境下,采用 HDFS+YARN 的方式作为自己底层大数据平台,仍然能满足绝大多数需求,也是最方便的解决方案。在十年前,Hadoop 就让大家享受到了阿姆达尔定律的红利,它的功劳还是需要被大家所铭记。在后面很长一段时间里,Hadoop 在大数据技术领域里仍然会占有一席之地。
总结
本节课主要讲解了 Hadoop 的架构及其一些关键组件等概念性的东西,但最后的比喻很有意思,作为集群的操作系统,Hadoop 短时间不会,未来也很难退出大数据的舞台。
Hadoop 2.0 与 Hadoop 1.0 最大的变化就是引入了 YARN,而 Spark 在很多情况下,往往也是基于 YARN 运行,所以,相比于分布式文件系统 HDFS,YARN 是一个比较关键的组件,承担着计算资源管理与调度的工作,所以本课时将对其进行深入讨论,先务虚再务实,主要内容如下:
统一资源管理与调度系统的设计;
统一资源管理与调度系统的实现——YARN。
统一资源管理与调度系统的设计
YARN 的全称是 Yet Another Resource Negotiator,直译过来是:另一种资源协调者,但是它的标准名称是统一资源管理与调度系统,这个名称比较抽象,当遇到这种抽象的名词时,我喜欢把概念拆开来看,那么这个名称一共包含 3 个词:统一、资源管理、调度。
来看看第 1 个词语:统一
对于大数据计算框架来说,统一指的是资源并不会与计算框架绑定,对于所有计算框架来说,所有资源都是无差别的,也就是说这个系统可以支持多种计算框架,但这是狭义的统一,我们理解到这里就可以了。而广义上的统一,是指资源针对所有应用来说都是无差别的,包括长应用、短应用、数据库、后端服务,等等。
来看看第 2 个词语:资源管理
对于资源管理来说,最重要的是了解对于这个系统,什么才是它的资源,或者说是资源的维度,常见的有 CPU、内存、磁盘、网络带宽等,对于 YARN 来说,资源的维度有两个:CPU 和内存。这也是大数据计算框架最需要的资源。
最后一个词语:调度
说到调度,就没那么简单了。目前的宏观调度机制一共有 3 种:集中式调度器(Monolithic Scheduler)、双层调度器(Two-Level Scheduler)和状态共享调度器(Shared-State Scheduler),我们一个一个来说:
集中式调度器(Monolithic Scheduler)
集中式调度器全局只有一个中央调度器,计算框架的资源申请全部提交给中央调度器来满足,所有的调度逻辑都由中央调度器来实现。所以调度系统在高并发作业的情况下,容易出现性能瓶颈,如下图所示,红色的方块是集群资源信息,调度器拥有全部的集群资源信息(蓝色方块),集中式调度器的实现就是 Hadoop MapReduce 的 JobTracker,实际的资源利用率只有 70% 左右,甚至更低。Jobtracker 有多不受欢迎呢,从 Hadoop 2.0 中 YARN 的名字就可以看出:另一种资源协调器,你细品。这种在多个计算作业同时申请资源的时候,中央调度器实际上是没有并发的,完全是顺序执行。
双层调度器(Two-Level Scheduler)
顾名思义,双层调度器将整个调度工作划分为两层:中央调度器和框架调度器。中央调度器管理集群中所有资源的状态,它拥有集群所有的资源信息,按照一定策略(例如 FIFO、Fair、Capacity、Dominant Resource Fair)将资源粗粒度地分配给框架调度器,各个框架调度器收到资源后再根据应用申请细粒度将资源分配给容器执行具体的计算任务。在这种双层架构中,每个框架调度器看不到整个集群的资源,只能看到中央调度器给自己的资源,如图所示:
紫色和绿色的圆圈所在的方框是框架调度器,可以看到中央调度器把全部资源的两个子集分别交给了两个框架调度器,注意看,这两个子集是没有重合的,这种机制类似于并发中的悲观并发。
状态共享调度器
状态共享调度器是由 Google 的 Omega 调度系统所提出的一种新范型,与谷歌的其他论文不同,Omega 这篇论文对详细设计语焉不详,只简单说了下大体原理和与其他调度范型的比较。
状态共享式调度大大弱化了中央调度器,它只需保存一份集群使用信息,就是图中间的蓝色方块,取而代之的是各个框架调度器,每个调度器都能获取集群的全部信息,并采用乐观锁控制并发。Omega 与双层调度器的不同在于严重弱化了中央调度器,每个框架内部会不断地从主调度器更新集群信息并保存一份,而框架对资源的申请则会在该份信息上进行,一旦框架做出决策,就会将该信息同步到主调度。资源竞争过程是通过事务进行的,从而保证了操作的原子性。由于决策是在自己的私有数据上做出的,并通过原子事务提交,系统保证只有一个胜出者,这是一种类似于 MVCC 的乐观并发机制,可以增加系统的整体并发性能,但是调度公平性有所不足。对于这种调度范式你可以不用深究,这里介绍主要是为了知识的完整性。
统一资源管理与调度系统的实现:YARN
前面一直在务虚,现在让我们来务实一下。YARN 是 Hadoop 2.0 引入的统一资源管理和调度系统,也很具有代表性,目前 Spark on YARN 这种模式也在大量使用,所以接下来,我们来讨论下 YARN。
简单来看看 YARN 的架构图,YARN 的架构是典型的主从架构,主节点是 ResourceManger,也是我们前面说的主调度器,所有的资源的空闲和使用情况都由 ResourceManager 管理。ResourceManager 也负责监控任务的执行,从节点是 NodeManager,主要负责管理 Container 生命周期,监控资源使用情况等 ,Container 是 YARN 的资源表示模型,Task 是计算框架的计算任务,会运行在 Container 中,ApplicationMaster 可以暂时认为是二级调度器,比较特殊的是它同样运行在 Container 中。
我们来看看 YARN 启动一个 MapReduce 作业的流程,如图所示:
第 1 步:客户端向 ResourceManager 提交自己的应用,这里的应用就是指 MapReduce 作业。
第 2 步:ResourceManager 向 NodeManager 发出指令,为该应用启动第一个 Container,并在其中启动 ApplicationMaster。
第 3 步:ApplicationMaster 向 ResourceManager 注册。
第 4 步:ApplicationMaster 采用轮询的方式向 ResourceManager 的 YARN Scheduler 申领资源。
第 5 步:当 ApplicationMaster 申领到资源后(其实是获取到了空闲节点的信息),便会与对应 NodeManager 通信,请求启动计算任务。
第 6 步:NodeManager 会根据资源量大小、所需的运行环境,在 Container 中启动任务。
第 7 步:各个任务向 ApplicationMaster 汇报自己的状态和进度,以便让 ApplicationMaster 掌握各个任务的执行情况。
第 8 步:应用程序运行完成后,ApplicationMaster 向 ResourceManager 注销并关闭自己。
结合这 8 步,再结合前面的调度范式,相信你已经对 YARN 的这种机制有了更深刻的理解。上面这 8 步,有些时候面试官会喜欢问,记住就行了。
好了,到现在为止,我都没有说明 YARN 到底属于哪一种调度范式,现在绝大多数资料与网上的文章都将 YARN 归为双层调度,这个说法准确吗?ApplicationMaster 与前面讲的框架调度器(二级调度器)很像,回答这个问题有点复杂,涉及到对调度范式得深刻理解,我简单讲下,你可以看看前面那种双层调度的范式图:
首先可以看到最下面的一个单词是 offers,还有蓝色和绿色的箭头方向,这说明什么问题呢?主调度器拥有整个集群资源的的状态,通过 Offer(主动提供,而不是被动请求)方式通知每个二级调度器有哪些可用的资源。每个二级调度器根据自己的需求决定是否占有提供的资源,决定占有后,该分区内的资源由二级调度器全权负责。
这句话怎么理解呢?如果你将集群资源看成一个整体,那么这种方式可以认为是预先将整个资源进行动态分区。作业则向二级调度器申请资源,可以多个作业共用一个二级调度器,此外,每个二级调度器和主调器都可以配置不同的调度算法模块。那么从这个点上来说,YARN 离真正的双层调度还有些差距,但和前面讲的 JobTracker 相比,已经是很大的进步了,并显著提升了调度性能,某度程度上,也可以说是一种双层调度,或者更准确地说,两次调度。所以如果在面试中,遇到这个问题,除非你和面试官都真的完全理解了 YARN 和双层调度的距离,否则还是回答 YARN 是双层调度吧。
由于 Spark 与 MapReduce 相比,是一种 DAG 计算框架,包含一系列的计算任务,比较特殊,所以 Spark 自己实现了一个集中式调度器 Driver,用来调用作业内部的计算任务。申请到的资源可以看成是申请分区资源,在该分区内,所有资源由 Driver 全权使用,以客户端方式提交的 Spark on Yarn 这种方式可以看成是 Driver 首先在资源管理和调度系统中注册为框架调度器(二级调度器),接收到需要得资源后,再开始进行作业调度。那么这种方式可以认为是一种曲线救国的双层调度实现方式,这个我们后面会讲到。
小结
本课时先介绍了三种调度范式:集中式、双层与状态共享,其中最常用的是双层调度模型,后面介绍了目前比较常用的一种统一资源管理与调度系统 YARN,Spark on YARN 也是非常常见的一种部署模式。由于 YARN 目前支持计算框架而不支持应用服务,可以看出,离真正的统一还有距离。此外,在绝大多数场景中,YARN 几乎都不会让你感觉到它的存在,所以对于分析师来说,本课时的内容你只需要了解就可以了。
在讲解具体技术之前,先来谈谈数据处理的场景,以及 Spark 在这些场景与流程中发挥的作用。技术要在特定的场景下才能发挥作用,那么在数据科学与数据工程中,有哪些场景呢,下面来看看这张图:
这张图从 3 个维度对目前常见的场景进行了分类,需要说明的是,由于场景这个概念不是一个易于定义且标准化的概念,所以并没有一个严谨且全面的分类方法,如图所示的分类方法并不十分严谨,其中的概念也有重合和交叉,但基本包含了数据处理中的所有场景,重合或者交叉之处会在后面说明。接下来,我们逐个进行分析。
在数据工程与数据科学中,很大一部分数据处理任务都可以被称为批处理(Batch Processing),所谓批处理,就是对数据进行批量处理,一次性对一定量的数据进行处理,根据数据量的大小,批处理从开始到结束的时间从数十秒到数小时都有可能,当然如果时间花费太长,还是会考虑优化、切分等,因为这样作业执行失败的成本太高了。批处理任务的输入和输出通常都是一批数据,在数据工程中常见的ETL场景中,经常会从数据库中抽取一部分数据进行去重后写入到存储系统,另外机器学习中训练模型都是典型的批处理。对于批处理来说,最大的缺点是数据处理任务延迟较长,无法与在线系统进行实时对接,但对于每条数据来说,消耗的计算成本是最低的。
而与批处理相对应的是流处理(Streaming Processing),与静止在某个系统中的批量数据不同,流处理在处理数据时数据是动态的,源源不断的,而且数据蕴含的价值会随着时间的流逝降低,所以需要对数据流进行实时处理。
流处理在数据工程领域运用比较广泛,一般都与在线系统对接,比如实时数据分析、业务系统的消息流转等。但在数据科学中,几乎没有流处理的场景,除了个别如在线训练这种比较特殊的应用。由于流处理可以认为是数据一到来就进行处理,所以对于每条数据来说,虽然延迟很低,但消耗计算成本是最高的。
这里举一个批处理与流处理结合的场景,比如模型分析师用机器学习算法对一批数据进行训练,得到一个模型,测试完毕后,数据工程师会将这个模型部署到线上环境对数据流进行实时预测。这也是数据科学与数据工程相结合的一个场景。
对于数据工程师与数据科学家来说,想要了解新的数据集最好的方式就是按照自己的习惯对数据进行一些查询处理,虽然这些查询处理的目的与方式都不同,比如数据科学家可能关注的是某一列的分布,从而发现一些有趣的东西,而数据工程师则关心的是某一列的异常值,进而修改自己的处理逻辑。但是这类查询处理都有一个共同点就是不确定,有可能根据数据集的不同而不同,也有可能根据用户的不同而不同,甚至下一个查询是基于上一个查询的结果,这类查询我们称之为数据探索。数据探索的第一个特点是不确定,而第二个特点是时间不能太长,如果太长的话,就会严重影响数据探索的效率也达不到探索的效果了。
与需求不确定的数据探索相对应的就是需求确定的数据处理任务,这类任务一般都会定时、定期运行,是公司、组织以及流程中的一部分,比如数据预处理、按照分析需求生成报表等等,通常再开发这类数据处理任务之前,会进行数据探索。
在这个维度下,按照结果响应时间分类,可以分为两类:
第 1 类通常指的是基于数据库操作或者是基于支持某种查询语言的工具(例如SQL)进行操作,并且实时返回结果,主要有两类:OLTP和OLAP,OLTP(Online Transaction Processing)通常指的是业务系统中常见的事务处理,对应数据库的增删改查操作,OLAP(Online Analytic Processing)主要指的是在线分析处理,对应数据库的查询操作,但不仅限于数据库,主要帮助分析人员可以迅速地、一致地、可交互地查询数据,也被称作交互式查询。
OLAP 与 OLTP 代表对数据处理两种截然不同的方式,但它们有个共同点,就是在线,这里在线意味着查询返回的结果不能太长,并且一般要能够支持在线应用,所以可以统称为在线处理。通常来说事务处理与分析处理分别代表了写优化与读优化两种方向,很难完全共存。
目前业界提出了一个新的场景和解决方案 HTAP(Hybrid Transaction and Analytical Process,混合事务与分析处理)系统,例如 TiDB 和阿里云的 AnalyticDB,既可以进行事务处理也可以进行分析处理,这里不展开介绍。
对于不能在线响应的场景,也就是第2类,这里笼统的称为离线计算或者离线处理,这里注意离线处理与在线处理界限并不是绝对的,对于同一个场景,如果全方位的进行优化,例如提升大幅度提升计算能力或者对数据进行预处理等,那么可以让原有的离线处理场景变为在线处理场景。
前面说到,这种分类方法存在一些概念的交叉与重合,很容易想到的,例如在数据探索中,会非常频繁地进行 OLAP,那么这类操作,我们一般称为即席(ad-hoc 查询)查询。在数据探索中,通常也可以忍受进行 1 分钟(或者更多)时间的批处理;数据处理任务中有可能有批处理,也有可能有流处理。
在上面的图中,除了 Spark 一般不会用于在线处理部分(OLTP、OLAP与HTAP)之外,在其他所有场景下,都能够很好的满足企业与用户的需求,但值得一提的是 Spark 与 OLAP 并不是完全没有关系,这里举一个例子:
在历史订单数据库中,保存了极其巨量的数据(从过去到现在的所有订单),而用户只关心历史某个品类的月度销量数据,但是由于原始数据过于巨大,所以导致普通的查询及其缓慢,在这里,可以用 Spark 将数据从数据库抽取出来并按照时间与品类维度进行转换和汇总(批处理),处理后的数据的大小与原始数据相比可能是上万倍的差距,用户就能很容易地进行在线分析了。
本课时主要是对数据处理场景进行一个简单的梳理,并介绍了一些概念,通过这种方式告诉大家 Spark 能做什么。另外,在讲到批处理与流处理的时候,提到了处理成本与延迟性的二律背反,这里大家留个印象,在流处理的章节中,会进一步提炼。
从下个模块开始,我们就会进入 Spark 的学习中,在正式开始学习 Spark 之前,首先需要选择自己要使用的 Spark 编程语言,了解如何部署 Spark,另外再根据选择搭建一个简单、方便的 Spark 运行环境。
本课时的主要内容有 3 块:
Spark 在诞生之初就提供了多种编程语言接口:Scala、Java、Python 和 SQL,在后面的版本中又加入了 R 语言编程接口。对于 Spark 来说,虽然其内核是由 Scala 编写而成,但编程语言从来就不是它的重点,从 Spark 提供这么多的编程接口来说,Spark 鼓励不同背景的人去使用它完成自己的数据探索工作。尽管如此,不同编程语言在开发效率、执行效率等方面还是有些不同,我将目前 Spark 各种编程语言优缺点罗列如下:
类型 | 开发效率 | 执行效率 | 成熟度 | 支持类型 | |
---|---|---|---|---|---|
Scala | 编译型 | 中 | 高 | 高 | 原生支持 |
Java | 编译型 | 低 | 高 | 高 | 原生支持 |
Python | 解释型 | 高 | 中 | 中 | PySpark |
R | 解释型 | 高 | 中 | 低 | SparkR |
SQL | 解释型 | 高 | 高 | 高 | 原生支持 |
现在我们对每个语言的优缺点进行详细的分析:
如果你才刚开始学习 Spark,那么一开始最好选择一门自己最熟悉的语言,这样 Spark 的学习曲线会比较平缓。如果从零开始,建议在 Scala 与 Python 中间选择,Scala 作为 Spark 的原生开发语言,如果想要深入了解 Spark 有必要掌握。
Python 开发速度方面的优势可以赋予开发人员极强的数据工程实践能力与数据科学探索能力,加上 Python 在数据科学领域的广泛应用,可以更好地发挥 Spark 在数据处理方面的优势。
基于以上原因,本专栏绝大多数例子,都会用 Scala 和 Python 语言实现。简言之,如果你是大数据工程师,以前比较熟悉 Java,那么建议选 Scala,除此之外,尤其是分析师,选 Python。
这里要特别说明的是,Spark 是由 Scala 开发而成,对于Java、Scala 编程接口来说,在执行计算任务时,还是由集群中每个节点的 JVM(Scala 也是 JVM 语言)完成。但是如果采用 Python、R 语言编程接口,那么执行过程是由集群中每个节点的 Python 与 R 进程计算并通过管道回传给 JVM,所以性能上会有所损失。
Spark 的编程语言,都属于 Spark 表现层面的东西,程序写好了,如何让 Spark 这个分布式架构运行起来,还有些工作要做,总结起来还需要 2 步:
我们在 03 课时介绍了统一资源管理与调度系统,作为计算框架的一员,同样的,Spark 也需要运行在某个统一资源管理与调度系统,目前 Spark 支持的统一资源管理与调度系统有:
Spark standalone 这种模式类似于前面讲的 Hadoop 1.0 的 MapReduce,由于缺点不少,基本不太适合在生产环境使用;Kubernetes 则是直到最新的 Spark 2.4.5 版本才支持;如果 Spark 运行在本地操作系统上,那么这就是我们说的伪分布模式,特别适合学习以及分析师用来处理中等数据量的数据,性能也还不错,当然这里指的是对单机性能而言。那么目前虽然支持 Spark on YARN 模式是目前最普遍的,但是 Mesos 才是 Spark 最先支持的平台,这里简单讲讲 Spark 是如何运行在 Mesos 上,你可以借此复习下前面的知识:
主要分 5 步:
一般来说,无论你基于公司的大数据平台进行开发还是分析,底层的统一资源管理与调度系统是什么对于工程师和分析师来说是无需关心的,对于代码来说没有任何不同,区别只体现在一些提交参数上。
前面提到,如果大数据平台使用了统一资源管理与调度系统,那么上层的计算框架就变成了这个资源系统的用户。这样做的结果是直接简化了计算框架的部署。对于部署计算框架这个问题,你可以用客户端/服务端,也就是 C/S 这种模式来理解。
我们把大数据平台看成是一个服务端,那么相应的就会有一些客户端,也就是一些节点,比如在 Hadoop 中,我们把这些客户端称为 Hadoop 客户端,你可以通过客户端访问 HDFS 或者提交作业。
所以,这些客户端也会有一份相应的安装包,按照客户端进行配置,Spark 也不例外,我们只需在客户端节点部署一份 Spark 安装包,并且正确配置,以YARN为例,需要你将YARN的配置文件复制到Spark客户端的配置文件夹下,就可以从该节点向大数据平台提交作业。提交的作业就会在集群中被调度为计算任务。
在学习之前,一定要准备一个便于学习和调试的环境,本课时我将带领你根据自己的需要搭建一个学习环境,也就是前面说的伪分布模式。对于选择 Scala 的用户来说,以伪分布模式运行 Spark 是一件很简单的事情,只需要在下面链接下载预编译好的 Spark 安装包,将里面的 jar 包导入到项目空间中就可以了,这个项目就可以作为你的学习环境,每次写好的代码也可以马上运行并得到结果。
http://spark.apache.org/downloads.html
还有一种方法,你可以用 Maven 项目来进行管理,这当然更好,我更推荐这种。
但对于 Python 用户来说,会稍微麻烦一点,这里将其总结为 5 步:
这个过程大概需要 15 分钟,现在我们开始吧。
安装之前可以先卸载以前安装的 Python,这样统一由 Anaconda 进行管理。Anaconda 是包管理器和环境管理器,对于 Python 数据分析师来说是必备软件之一,我们可以在官网根据不同的操作系统下载对应版本(都选择 Python 3.7):
安装完成后,我们就可以在控制台使用 pip 命令了。
Jupyter notebook 是一个交互式的 Web 笔记本应用,可以支持多种编程语言,事实上 Anaconda+Jupyter notebook 已成为数据分析的标准环境。那么 Jupyter notebook 还有一个非常适合的场景,就是教育,它的笔记本特性可以非常好地将学习过程固化。由于前面我们已经安装好了 Anaconda,所以安装 Jupyter notebook 只需要执行下面这两条命令即可:
pip install --upgrade pip
pip install jupyter
现在通过 Anaconda 安装 PySpark 已经很方便了,只需要在控制台执行如下命令:
pip install -U -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark
我在里面换了一个清华的源,国内用户会快一点。另外要注意的是,直接执行这条命令有可能会安装失败,Windows 用户需要以管理员身份运行控制台,再执行命令。如下图:
Mac 用户可以用 sudo 前缀执行该条命令,如下:
sudo pip install -U -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark
前面几步完成后,我们就可启动 Jupyter notebook。在控制台执行以下命令:
jupyter notebook --ip=0.0.0.0 --notebook-dir='E:\\JupyterWorkspace'
需要注意文件夹要事先创建好,这个就是你的笔记本文件夹。启动后,浏览器会弹出,可以在控制台里面找到Jupyter notebook的链接,如下图所示:
将链接复制到浏览器中,就可以使用 Jupyter notebook 了,如下图所示:
为了测试安装的结果,我们新建一个笔记本,在单元格中写入如下代码:
from pyspark.sql import SparkSession
from pyspark.sql.functions import col
##初始化
spark = SparkSession.builder.master(“local[*]”).appName(“Test”).getOrCreate()
spark.range(0, 5).select(col(“id”).cast(“double”)).agg({‘id’: ‘sum’}).show()
spark.stop()
代码的作用是 Spark 对元素为 0~4 的数组进行求和处理,运行代码,Jupyter notebook 会展示运行结果。如下图所示:
这里特别说明的是,代码中的参数 local[*] 指明了 Spark 基于本地操作系统运行,如果基于 YARN、Mesos 或者 Kubernetes,只需要对应修改该参数即可。
本课时主要介绍了 Spark 编程语言以及选择建议,并结合第 3 课时的内容,介绍了 Spark 部署与提交作业的方式,并帮助你搭建一个方便的学习 Spark 的环境。
学完了本课时的内容,这里还有一个小问题留给你:前面说到伪分布模式的参数为 local[*],那么是否可以将 * 替换为具体数字,如果可以,会引起怎样的变化呢?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。