前言

前言

近些年,大数据的概念非常火,且许多行业已经开始从大数据中获益,比如广告业。越来越多的企业也正在尝试使用大数据的平台和方法来解决一些现实问题,不过,在这个过程中难免会遇到困难。因此,本书旨在提供一个非常具有可操作性的指引,帮助读者亲身体验大数据的魅力。

Spark是最近几年开始流行的一种通用分布式大数据计算平台,无论是性能、功能还是易用性,都在众多大数据技术中名列前茅,可以很好地解决大数据领域的许多常见问题。本书循序渐进地介绍Spark的基本概念、核心思想、部署、开发,并提供多个典型场景的解决方案,试图让即便是零基础的Spark读者也可以从中受益,并让对Spark已有所知的读者可以更深入地了解其运行机制及精髓。

没有大数据平台的支持,普通海量数据的计算分析极端低效

笔者2008年毕业后即进入腾讯公司工作至今,刚进公司时感觉真是“一夜暴富”,因为名下的服务器有上百台,日常工作除了后台应用程序的开发,还要担负一些运营工作。然而,维护上百台机器上产生的日志文件就是一个非常令人头痛的问题。虽然每台机器都有TB级的硬盘空间,但还是经常爆满,我们经常半夜被电话吵醒起来删文件。此外,产品人员经常提出一些数据统计分析的需求,然而数据分布在上百台机器上,统计效率非常低下。

面对这样大量数据的统计,最常用的一种处理模式是,选择一个字段对数据进行散列化,然后按散列值的不同分别存储在单独的MySQL数据库表中,而这些MySQL节点分布在上百台机器上。需要统计的时候,我们再写个脚本分别去各个MySQL节点上取数据,或者简单处理一下,然后再汇总到一台机器上做处理。

这种方法简单易行,但问题很多。最大的问题是计算过程复杂,开发统计脚本的时间以天计,因为原本一个SQL就可以搞定的事情,现在需要分多步进行,且中间还需要将大量数据在多个节点上进行传输。比如这个简单的案例,它需要对数据按IP进行分布统计,计算过程至少需要3步:

(1) 去各个MySQL节点上执行SQL,按IP汇总统计;

(2) 数据汇总到一个节点上,导入MySQL;

(3) 在汇总的MySQL节点上进行统计。

而如果数据没有拆分存储,一个SQL就可以搞定。这只是一个最简单的例子,如果统计更复杂一些,这样的“计算-汇总-计算”的过程可能需要进行多次。

这种方法带来的第二个问题是散列分布不均匀导致的计算瓶颈和资源浪费。为了方便查数据,散列的规则设计不能太复杂,一般是取某个整数的十进制数值的前3位或末3位。这样的好处是日常定位数据位置很方便,缺点是数据分布不均匀,总有一些节点会承载非常多的数据,而另外一些节点又承载非常少的数据。承载数据多的机器会成为整体计算的瓶颈,而承载数据少的机器总是空闲,从而造成资源的浪费。

所以整体看来,这种方法的效率非常低下,计算本身耗时虽然不多,但开发过程相当费时,如果逻辑更复杂,甚至需要一周时间。

Hadoop和Hive的组合,可以解决大数据计算的许多常见问题

后来我们引入“Hadoop + Hive”的解决方案,问题改善了很多。数据全部存储在Hadoop上,在各节点上均匀分布。在Hive上创建表并导入数据,设置好分区,这样虽然数据的存储还是分布式的,但从数据统计角度来看像是一个数据库,而不是之前的上千个数据库表。这样,开发者只需要关注自己的业务逻辑,而不需要关注底层数据的存储、分区等信息,开发效率大大提升。虽然Hive的计算效率不是很高,运行时间可能是“脚本 + MySQL”方式的数倍,但一般也只需要几十分钟,而节省的开发成本是以“天”计的,整体效率大大提升。

然而,随着数据量的继续快速上升,以及大家对数据的关注度的提高,Hive平台上的计算量越来越大,人们对于计算的需求也在变化。具体来讲,一是统计规则越来越复杂,原来几行SQL代码可以搞定的统计越来越少,新的统计需求动辄需要上百行SQL代码才能完成,而且量越来越多;二是越来越多的计算是SQL完成不了的,需要借助机器学习算法或图计算来进行。

这些新需求带来的挑战逐渐让“Hadoop + Hive”方式有些吃不消,一些问题逐渐暴露出来:一是性能不够高,一是简单的MR计算模式支持的应用场景非常有限。

Spark出现之后,性能、功能、易用性都有质的提升

Hadoop的这些问题暴露之后,业界也有一些针对性的优化方案。但Spark走得更远,在参考MR的基础上重新实现了一套通用计算框架,性能提升10~100倍,尤其是在机器学习等复杂迭代计算的场景下,性能提升最明显。另外,使用场景除了SQL之外,Spark还支持类似Storm的实时流式计算和图计算,机器学习库也更丰富,为开发带来了极度的便利。因此,越来越多的技术人员开始使用Spark来替代Hadoop的MR计算,用Spark SQL来替代Hive,但存储依然使用Hadoop HDFS。

本书会详细介绍Spark,值得注意的是,Spark在设计伊始就充分考虑到了用户体验问题。Spark使用Scala语言开发,支持函数式编程,让代码非常简洁。比如,Hadoop MR编程中的示例程序WordCount,在Hadoop下用Java实现时代码有上百行,而且还需要经过编写代码文件、编译、上传执行等过程,而Spark集成的Scala语言支持交互式编程,进入交互式模式后直接输入代码即可开始执行,并且代码只有区区几行:

text_file = spark.textFile("hdfs://...")
text_file.flatMap(lambda line: line.split())
    .map(lambda word: (word, 1))
    .reduceByKey(lambda a, b: a+b)

交互式编程配合函数式编程,使得Spark编程非常容易上手。笔者曾经考虑要不要让Spark支持bash这类脚本语言,但最终觉得Scala足矣。本书附录提供了Scala语法的简单参考,相信有一定编程基础的读者会非常容易上手。

经常有人会问:为何Spark会比Hadoop快那么多呢?我也曾反复思考过,在此抛砖引玉了。Spark和Hadoop都是非常优秀的开源项目,代码实现及架构已经非常优秀了,差别应该不在实现层面。大多数人认为Spark比Hadoop快是因为大量使用内存,正因此,Spark一开始被称作内存计算,但我认为还有一个更重要的原因,那就是核心数据结构。Spark使用RDD(弹性分布式数据集,读者可以将其想象成一个分布式的大数组)作为核心数据结构(相比之下Hadoop没有核心数据结构,只有Map/Reduce计算模式),在此基础上提供了许多计算函数,类似Hadoop下的Map/Reduce函数,但数量更多,还可以无限扩充。这样的好处是Spark的任务粒度更小,原先在Hadoop下一个Map或Reduce实现的功能,在Spark下可能会被拆分成多个Job。如果把Hadoop中的任务比作罐子里面的石头,那么Spark的Job就是罐子里面的碎石子,因此相同罐子可以装得更多。Spark这样细粒度的任务调度,再配合上内存的充分利用,最终让性能提升了一个台阶。

还有一个有意思的现象:大数据领域的技术很多是基于Java开发的,Hadoop、Hive、HBase都是,Spark使用的Scala最后也是转化成JVM字节码运行。Hadoop虽然被诟病性能低,但也没有基于C++的开源版本出现,究其原因,除了大家经常提到的这些开源系统的创始人喜欢Java,以及Java对于跨平台的强有力支持,我猜测可能还有如下几个原因。一是因为Java在大型系统开发方面的便利性,有非常多的库可以使用。这样一来,在系统最初的探索阶段可以节省大量人力,而Spark使用的Scala语言因为支持函数式编程,代码量进一步精简了好几倍,比如Spark中的SQL优化执行引擎只用2000行左右的代码就实现了,而且表现接近于手写代码,完全受益于语言在开发上的便利性。二是因为大数据计算场景下,系统的瓶颈经常不在于计算,而是网络传输和磁盘读写,此时CPU反而不是瓶颈,所以无论Hadoop和Spark都在这方面做了大量优化,语言性能方面的弱势不那么明显了。比如Hadoop和Spark中最重要的shuffle机制,就是为解决网络传输而设计的,而且不断优化,每次优化都可以在整体上提升性能。三是在Hadoop和Spark这类通用计算平台上都遵循一个理念,就是“宁可移动计算,不要移动数据”,这正是Java语言强大的序列化功能发威的时候。当然C++也可以实现,性能可能会更优,但开发成本可能不是一般小公司能承受得了的,也只有谷歌这样技术、财力雄厚的公司才有可能实现。事实上,Hadoop的理论基础也来自谷歌的论文。

大数据平台众多,我们要怎么选呢

大数据热门之后出现了非常多的大数据技术,有Hadoop、Hive、HBase、Flume、Kafka、Lucene、Spark、Storm等,还有很多NoSQL技术可以归入大数据,比如MongoDB、CouchDB、Cassandra等。有人列举过大数据技术族谱,其中至少上百种技术。面对如此多的技术,我们往往会产生疑惑:到底该如何选择?这里有一个建议:在充分理解自身需求的基础上,挑选最合适的。

比如要实现这样一个任务:有10亿条用户画像数据,里面记录了用户的性别、年龄、地域等信息,输入一个用户包,只包含用户账号,数量平均百万级,最多1000万,希望查询这些用户的性别、年龄、地域等属性的分布信息。

面对这样的问题,如果我们贸然找一个开源系统部署上,刚开始的学习成本高不说,即便可以用,最后也不一定能证明这个方案到底是好还是不好。因为从来没有最好的方案,只有当前最合适的方案,不但要跟其他方案比,还要跟问题本身的需求比。比如,一开始就有一些有经验的前辈建议使用Solr或ElasticSearch,这两个系统固然非常强大,但大家都知道是为搜索引擎这样的场景设计的,而不是为这个问题场景设计的,中间难免有些匹配度的问题,不一定最合适。而且,如果不分析一下面对的问题场景,那么在研究Solr或ElasticSearch时也没有针对性,只能“全面开花”,结果必然费时费力,可能还没有好的结果。要知道,单Solr的文档就有几百页,全部看完至少需要一周时间。

因此我们先来分析一下当前的问题。如果所有数据都在数据库中,那么这个计算可以用SQL实现(以统计“性别”属性为例,其他与此类似):

select
    性别, count(1)
from
    用户号码包表(1000W) join 画像表(10亿)
group by
    性别

显然计算的瓶颈在于join操作,join之后需要处理的数据量从10亿下降到1000 万,这可以大大降低后续的聚合统计的成本。而对于join操作来说,如果两边的数据都已经排好序,那么join的算法复杂度只是O(n),非常低。由于10亿条画像数据更新频率很低,我们可以提前做好排序预处理,因此最理想的计算过程可以分为这么几步:

(1) 对输入的用户号码包进行排序;

(2) 排序后的号码包与已经有序的画像数据进行join,筛选出号码包的属性;

(3) 对筛选后的少量数据进行聚合统计。

有了对问题的理解,我们再简单对比一下各种方案的优劣(使用相同的机器,集群数量为10台),参见下表。

方案实现过程总用时缺点
Hive集群(10台)(1) 画像数据和号码包都存储为一张表;
(2) 对两张表进行join操作,筛选出号码包的属性;
(3) 对每个属性进行统计
> 1小时机器资源占用多,维护成本高
Spark SQL(10台)同Hive10分钟
Spark MR(10台)思路同上,但可以让画像数据提前排好序,并且两张表使用相同的分区策略,这样可以提升join的效率8分钟
Bash (单机单核)(1) 对号码包进行排序;
(2) 号码包文件与有序的画像文件进行join,筛选出号码包的属性;
(3) 对筛选出的结果按维度分别统计
12分钟单点
Bash(单机多核)思路同上,但把大文件拆成小文件,并发执行3分钟
磁盘替代CPU成为瓶颈,如果有多个物理磁盘,性能可以进一步提升至1分钟
从零开发一个专有分布式系统(3~10台)思路同上,但所有的操作都在内存中,可能需要多台机器< 0.5分钟开发成本太高
对开发人员要求高
Solr(3台机器)(1) 设置集群为多shard结构,画像数据提前导入;
(2) 新的画像数据作为新的字段导入;
(3) 使用join操作和Facet特性对各个维度分别统计
1分钟没有筛选,每个维度的查询都搜索了所有10亿画像数据
Facet特性必须指定一个UniqueKey,导致号码包导入时需要多处理一个UniqueKey字段

通过分析不难发现,有很多技术可以解决这个问题,连最不起眼的bash都可以,而且单机的性能就接近甚至超过10台Spark集群的性能。如果单独开发一个专有系统,性能更是会得到指数级提升。

然而,通过这些对比依然不足以确定我们的最终方案,因为对于需求本身还有很多疑问,比如系统的访问频率、扩容要求、需求变更,甚至还有团队的研发能力等。如果回答了这些问题,那么最终的决策只是水到渠成的事情。

本书内容

全书共分8章,外加一篇附录。前4章介绍Spark本身,包括部署、工作机制、内核等。全书的重点在第5章~第8章,每章不但深入浅出地介绍Spark的一个功能模块,而且包含一个实战项目,项目利用国内互联网的真实数据为案例,搭建一个产品和平台输出。这些例子每个都可以是一个独立的大项目。这在BAT等公司中,动辄都是有几十上百人的项目团队,而笔者基于Spark快速搭建了一个原型,为创业者提供了很好的入门示例。此外,书中还详细介绍了各种可能遇到的实战问题,比如大数据环境下的配置设置、程序的调优等。随书带一键安装脚本,以便为初学者提供很多帮助。

  • 第1章概述大数据的发展状况,以及Spark的起源、特点、优势、未来等。

  • 第2章介绍Spark部署和编程。作者首先带领读者在本地单机下体验Spark的基本操作,然后部署一个包括ZooKeeper、Hadoop、Spark的可实际应用的高可用集群。并且,这一章还介绍了笔者合作开发的一个自动化部署工具,最后介绍了Spark编程的基础知识,以及如何打包提交至集群上运行。

  • 第3章介绍Spark底层的工作机制,包括调度管理、内存管理、容错机制、监控管理以及Spark程序配置管理,这对理解Spark程序的运行非常有帮助。

  • 第4章深入Spark内核,并结合源码,介绍了核心结构RDD、RDD对象的Transformation和Action操作是如何实现的、SparkContext对象及初始化过程、DAG调度的工作流程。了解这些内容可以帮助读者编写出高质量的Spark程序代码。

  • 第5章介绍Spark SQL,可以代替Hive,用于搭建一个企业级的数据仓库。案例基于淘宝的电商数据建立电商数据仓库,并以日常运营工作为例,通过电商数据库分析电商运营中的各类问题。

  • 第6章介绍Spark实时流式计算,类似于Storm,但吞吐量方面更有优势。案例是基于一个站点的Web日志建立一个类似百度统计的实时统计系统,是各种实时系统典型的参考例子。

  • 第7章介绍Spark的图计算。案例基于新浪微博2000 万的关系链数据,讲解了如果利用图计算来实现社交关系链的挖掘,比如闺蜜的发现、粉丝团伙的发现等。

  • 第8章介绍Spark的机器学习库。案例基于某个搜索引擎的点击日志,建立了一个搜索广告点击率预估系统。广告点击率预估是各家互联网系统的核心系统,公开的实战项目不多。

  • 附录为Scala语言参考,方便第一次接触Scala语言的读者快速上手,体验Spark编程。

致谢

本书由陈欢和林世飞一起合作编著,大家年龄相仿,志同道合,牺牲了很多陪家人的时间才完成本书的撰写,非常感谢家人的支持。还要特别感谢腾讯企业云的“攻城之地”活动提供免费服务器,感谢李冠灿为服务器使用提供很多便利,感谢李学凯、余衍炳等人审阅了部分章节,提供了许多宝贵意见。感谢王流斌在如何使用KDD2012数据集方面提供了不少建议。感谢腾讯公司提供的学习和成长环境,以及各位领导对于我们撰写本书的鼓励和支持。感谢UCloud的CEO季昕华提供宝贵建议,并撰写“推荐序”。成稿期间,图灵出版公司的王军花编辑和毛倩倩编辑给予了极其详细的改进意见,在此表示由衷的感谢。

由于作者水平有限,书中难免有错误和不当之处,欢迎专家和读者给予批评指正,来函请发至sparkbestpractices@qq.com。

 

2016年2月

目录

  • 序一
  • 序二
  • 前言
  • 第 1 章 Spark与大数据
  • 第 2 章 Spark基础
  • 第 3 章 Spark工作机制
  • 第 4 章 Spark内核讲解
  • 第 5 章 Spark SQL与数据仓库
  • 第 6 章 Spark流式计算
  • 第 7 章 Spark图计算
  • 第 8 章 Spark MLlib
  • 附录 Scala语言参考