总结Google的三驾马车
前面的三篇文章,我们谈了Google的三驾马车,现在我们总体地对它们进行一个总结。
要想对这三篇文章作总结,那么就必须要对这三篇文章的背景更加仔细地了解。文章开头的介绍是从总体的角度来看的,但是我最近看到一篇关于Jeff Dean的文章,从Jeff Dean的角度更加详细地介绍了当时的困局。我下面将引用一些该文章的中内容,并以此讨论三架马车的设计出发点。
2000 年 3 月的一天,谷歌公司最顶尖的六位工程师齐聚某临时“作战指挥室”。当时的谷歌,正面临着前所未有的紧急状况。前一年 10 月,谷歌用于爬取 Web 以建立网络内容索引的核心系统宣告停止工作。虽然用户仍然可以通过 http://google.com 网站进行结果查询,但他们收到的结果实际上已经过期了五个月。由此引发的利益冲突远超工程师们的想象……就在当时,互联网的体量在一年之内增长了一倍。
相当有趣的是,哪怕谷歌的爬虫已经崩溃了快半年,所有的查询结果都是六个月之前的,他们仍然敢和雅虎谈合作。也就是当时互联网内容更新不够快,要是放在今天,一个星期就够谷歌倒闭了。
在作战室中度过的第五天,Jeff 与 Sanjay 开始怀疑其中的问题不是出在逻辑层面,而属于物理范畴。他们将混乱的索引文件转换为最原始的表示形式:二进制代码。更具体地讲,他们想了解机器到底看到了什么。
Sanjay 的显示器上出现了一系列粗体的 0 和 1,每一行代表一个索引词。Sanjay 发现,其中某个本应是 0 的数位却显示成了 1。接下来,Jeff 与 Sanjay 将所有错误排序的词语汇总了起来,并发现了其中的共通模式——每个词语都存在相同的问题。他们机器上的内存芯片发生了故障。
Sanjay 不由自主将目光投向 Jeff。几个月以来,谷歌一直在经历各种各样且持续增加的硬件故障。问题是,随着谷歌业务的快速发展,其计算基础设施也在不断扩大。计算机硬件发生故障的概率一般是很低的,因此问题堆积起来之后,一下子引发了破坏性的影响。电线磨损、磁盘损坏、主板过热。相当一部分设备根本无法一次性成功启动,也有一些莫名其妙地速度变慢了。
我们都会使用“搜索 Web”或者“搜索网络”的说法,但实际上这种表述并不准确。实际上,我们的搜索引擎遍历的是 Web 的索引——或者说地图。在 1996 年还在使用 BackRub 这个名号时,谷歌的索引地图还很小,足以安装在 Page 宿舍中的个人电脑里。但到 2000 年 3 月,其规模已经超出了任何超级计算机的处理能力。
要跟上如此迅猛的发展速度,谷歌公司唯一的方法就是购买消费级设备并将其组成一个集群。由于消费级设备当中有半数成本来自对谷歌公司毫无意义的“垃圾”——包括软盘驱动器与金属机箱,因此他们决定直接订购主板与磁盘并将其连接起来。谷歌在加利福尼亚州圣克拉拉市的一栋楼里将 1500 套这样的设备堆到了六英尺高; 但由于硬件故障,其中只有 1200 套设备能够正常工作。另外,随机发生的故障也在不断破坏着这套系统。为了维持业务运转,谷歌方面必须将这些计算机构建成一套无缝且具备弹性的整体。
从这里,我们一方面可以看出Jeff和Sanjay确实厉害,一般人最多查到逻辑层面,现在他们直接怀疑是硬件的问题。另一方面,这也说明了为什么GFS,Bigtable和MapReduce都要将容错(Fault-Tolerance)作为重要的考虑因素,并且都考虑最坏的情况,甚至在GFS中使用了多个备份的机器。
这里必须要吹的是,GFS能充分发挥这么一堆“商业垃圾”的性能实在是强得可怕。哪怕是读过GFS的论文,对GFS有了初步的了解,我也很难想象它能在这么差劲的机器上撑起足够能用的存储体系,而且还能够保证数据的完整性。
在 2003 年的四个月当中,Jeff 与 Sanjay 帮助谷歌完成了创立以来规模最大的一次升级。他们所使用的软件,就是后来赫赫有名的 MapReduce。这次升级的灵感,来自他们在对谷歌爬取工具与索引器进行第三次重写的过程。他们意识到,每一次他们解决一个重要问题,所面向的都是地理分布广阔且个别设备可能不太可靠的无数计算机上协同运行。因此,只有对解决方案进行全面推广,才能避免一次又一次重复面对同样的问题。更具体地讲,应该创建一款工具,确保谷歌公司的每一位程序员都能够利用其运行数据中心内的机器——换言之,将谷歌的所有基础设施视为一台硕大无朋的整体计算机。
MapReduce 的意义在于把可能令人费解的复杂流程整理得井然有序。在 MapReduce 出现之前,每一位程序员都必须弄清楚要如何对数据进行分割与分发,分配工作并自行负责硬件故障的处理。MapReduce 为编程人员提供了一种用于考量此类问题的结构化方法。正如厨师在食材下锅之前要先对其进行分类一样,MapReduce 也要求程序员将自己的任务分成两个阶段。首先,程序员需要告诉每台机器如何完成任务的“Map”阶段(比如计算某个词语在网页中出现的次数); 接下来,程序员要编写指令以实现全部机器结果的“Reduce”(例如将上述结果相加)。MapReduce 可以处理分发工作的细节,从而帮助程序员摆脱这些复杂且枯燥的任务。
在接下来的一年当中,Jeff 与 Sanjay 以 MapReduce 任务的形式重写了谷歌的爬取与索引系统。很快,当其他工程师意识到 MapReduce 的强大力量后,他们也开始利用其处理视频并在谷歌地图上渲染图块。MapReduce 非常简单,能够轻松消化各类新型任务。谷歌的业务有着明确的所谓“昼夜使用曲线”——即白天的流量比晚间更多,而 MapReduce 任务则开始使用闲置部分的容量。正如生物会在梦中回顾白天的经历,现在谷歌也在利用同样的方式处理自己的数据。
从这里,我们就可以完全理解MapReduce的设计逻辑了。在这三篇文章发布的时候,谷歌几乎所有的重心都在搜索引擎上面,谷歌搜索引擎的核心就是PageRank算法,而URL之间的引用次数是PageRank重要的一部分。MapReduce要做的就是计算网页直接的引用次数,所以他们简单地将爬取的数据作为输入文件交给Map处理,当MapReduce都得到结果后,他们就能得到相关网页的引用次数,然后将其作为参数传入到PageRank中就能得到想要的答案。
另外,由于夜间的流量较低,所以他们可以在晚上运行MapReduce程序而不影响用户体验。这也就解释了为什么谷歌能够容忍那么长的计算时间。
简单地说,MapReduce之所以简单是因为它负责的计算就很简单,但是计算量很大,所以使用分布式的架构。MapReduce慢是因为谷歌根本不需要它快。所以MapReduce可能仅仅适用于谷歌和一些业务场景符合的公司。
这篇文件中没有提到Bigtable,而且看起来MapReduce可以调用GFS而无需访问Bigtable,那么为什么Bigtable仍然成为人们津津乐道的技术,甚至在后来的Spanner中谷歌也使用了相当一部分的设计呢?那就是,数据库在业务逻辑中是无可替代的。
数据库提供的不仅仅是数据的存储,更重要的是数据的读取。这里的存储和读取,不仅仅是完整的一致的,还要求是快速的。假如我们直接使用GFS,那么我们就没有完整的数据视图,需要像查文件一样查找数据,不仅效率低下,而且指定文件路径在编程上也是十分死板的做法。另一方面,GFS在数据写入方面也完全没有优化的处理。
而在Bigtable中,由于Bigtable保证数据有序,那么读取和查找数据的效率会指数级别地提高。在写的方面,它使用WAL保证容错,使用Memtable保证高效,是GFS所不能比的。另一方面,它使用Column Family实现了类似于事务的功能,能够实现ACID的要求,这也是业务所强烈要求的。
而且我相信,根据Bigtable的数据模型来看,爬虫爬下来的网页数据应该不是直接存入GFS,而是先存入Bigtable,然后通过Bigtable保存到GFS上的。因为Bigtable的数据模型怎么看都像是为了存储网页直接相互引用的信息设计的。而且,由于MapReduce产生的数据有可能在未来也被使用,这些数据被存入GFS。
那么我们最后来总结一下三者的调用关系。谷歌使用爬虫爬取网页信息,并且把这些信息存入到Bigtable中,Bigtable会将这些数据排序并保存到GFS中。MapReduce为了得到PageRank的参数,会调用Bigtable中的数据,计算各网页的引用次数。计算完成后,MapReduce会将这些数据保存到GFS中以待下次使用。
如果要对这三驾马车作出一个评价的话,那么应该这么说:在谷歌危急的时候,这三驾马车不仅力挽狂澜,而且为谷歌和其他互联网企业的发展指明了未来,是分布式系统的重要基石。