大数据系统构建:可扩展实时数据系统构建原理与最佳实践(txt+pdf+epub+mobi电子书下载)


发布时间:2020-11-26 09:13:02

点击下载

作者:(美)南森·马茨(NathanMarz),(美)詹姆斯·沃伦(JamesWarren)

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

大数据系统构建:可扩展实时数据系统构建原理与最佳实践

大数据系统构建:可扩展实时数据系统构建原理与最佳实践试读:

前言

当第一次进入大数据的世界时,我仿佛置身于软件开发的美国西部荒原。许多人放弃了关系型数据库,转而选择带有高度受限模型的NoSQL数据库,主要是因为其使用体验良好、熟悉度较高且这种数据库可以扩展到成千上万台机器上。NoSQL数据库的数量巨大,堪称铺天盖地,这些数据库中很多都只有细微的差别。一个名为“Hadoop”的新项目开始崭露头角,它宣称具备基于海量数据进行数据深度分析的能力。但弄清楚如何使用这些新工具很令人困惑。

当时,我正试图处理所在公司面临的扩展性问题。系统架构非常复杂——该Web系统包含共享关系型数据库、队列、工作节点、主节点和从节点。数据损坏渗透至数据库,为了处理这些损坏,我们使用了应用程序中的特殊代码,但从节点的操作总是落后于其他节点。我决定探索其他大数据技术,看看是否有比我们的数据架构更好的设计。

早期的软件工程职业生涯的经历,深刻影响了我对“系统该如何架构”的观点。我的一位同事花了几个星期将来自互联网的数据收集到一个共享文件系统。他在等待收集足够的数据,以便能在其上进行数据分析。有一天,在做一些日常维护时,我不小心删除了他的所有数据,导致他的项目延期了好几周。

我知道自己犯了一个大错,但作为一个软件工程师新手,我并不知道这会导致什么样的后果。我会不会因为粗心被解雇呢?我发了一封电子邮件向团队诚挚地道歉——让我惊喜的是,大家对此都表示非常同情。我永远不会忘记那个时刻——一个同事来到我的办公桌旁,拍着我的背说:“恭喜你!你现在是一个专业的软件工程师了!”

他玩笑式的表述道出了软件开发中不言而喻的“真理”——我们不知道如何创造完美的软件。软件可能有bug而且会被部署到生产中。如果应用程序可以写入数据库中,那么bug也可能写入数据库中。当着手重新设计我们的数据架构时,这样的经历深深地影响了我。我知道,新架构不但必须是可扩展的、对机器故障是可容错的,并且要易于推断故障原因——但对人为错误也可容错。

重构那套系统的经验,促使我走上了一条“在数据库和数据管理方面怀疑一切我认为是正确的”道路。我想出了一个基于不可变数据和批量计算的架构,令我很惊讶的是,与仅仅基于增量计算的系统相比,新系统要简单得多。一切都变得更容易,包括操作、不断发展的系统以支持新的功能、从人为错误中恢复和性能优化等方面。该方法很通用,似乎可以用于任何数据系统。

但有些事情困扰着我。当观察其他行业时,我发现几乎没有人使用类似的技术。相反,在使用基于增量更新数据库的庞大集群架构中,令人生畏的复杂性是为人所接受的。这些架构的许多复杂性已经通过我所开发的方法完全避免或大大缓减了。

在接下来的几年中,我扩展了该方法,并使之正式成为我戏称的Lambda架构。在初创公司BackType工作时,我们的5人团队构建了一个社会化媒体分析产品,该产品支持在超过100TB的数据上进行多样化实时分析。我们的小团队还负责拥有数百台机器的集群的管理部署、运营和系统监控。当我们向别人展示自己的产品时,他们对这个团队只有5个人感到非常惊讶。他们经常会问“这么几个人做了这么多事情?怎么可能!?”我的回答很简单:“不是我们在做什么,而是我们没有做什么。”通过使用Lambda架构,我们避免了困扰传统架构的复杂性。通过避免这些复杂性,我们大大提高了工作效率。

大数据运动只是放大了已经存在了几十年的数据架构的复杂性。主要基于增量更新的大型数据库架构将遭受这些复杂性的折磨,从而导致错误、繁重的操作,并阻碍了生产力。尽管SQL和NoSQL数据库通常被描述成对立或相互对偶的关系,但从最基本的方面来说,它们实际上是一样的。它们都鼓励使用这种相同的架构——该架构具有不可避免的复杂性。复杂性是一个邪恶的野兽,无论你承认与否,它都会“咬”你。

为了传播Lambda架构以及它如何避免传统架构的复杂性等知识,我写了本书。它是我开始从事大数据工作时就希望有的。我希望你把这本书作为一个旅程——挑战你以为自己已经知道的关于数据系统的知识,并发现从事大数据工作也可以优雅、简单和有趣。Nathan Marz关于本书

类似社交网络、网络分析和智能型电子商务这样的服务,通常需要在非常大规模的传统数据库上管理数据。复杂性随着规模与需求的增加而增加,而处理大数据并不是简单地将RDBMS扩大一倍或推出一些时髦的新技术。幸运的是,可扩展性和简单性并不是相互排斥的——你只需要采取不同的方法。大数据系统使用多台机器并行工作来存储和处理数据,它引入了大多数开发者并不熟悉的根本性的挑战。

本书将教你充分利用集群硬件优势的架构,以及专门用来捕获和分析网络规模数据的新工具,来创建这些系统。它将描述一个可扩展的、易于理解大数据系统的方法,可以由小团队构建并运行。本书利用一个实际示例,基于大数据系统的理论在实践中实现它们来指导读者。

本书不要求读者以前接触过大规模数据分析或NoSQL工具。熟悉传统数据库是有帮助的,但不是必需的。本书旨在教你如何思考数据系统,以及如何化繁为简。我们将从基本原理开始,从那些被认定为架构的各个组件所必需的属性开始。路线图

本书包括18章,各章的主要内容如下。

第1章介绍了数据系统的原理,并给出了Lambda架构的概述:构建任何数据系统的广义方法。第2~17章以理论和示例交替讲解的方式深入介绍Lambda架构的所有内容。理论章节阐述了若干概念,这些概念对现有工具都是适用的;同时,示例章节使用现实世界中的工具来论证这些概念。不要被名字迷惑,虽然所有章节都是样例驱动。

第2~9章集中阐述Lambda架构的批处理层。在这里,你将了解如何为主数据集建模、如何使用批处理来创建数据的任意视图,以及如何进行增量和批处理之间的权衡。

第10章和第11章集中阐述服务层,它支持低延迟访问由批处理层中产生的视图。在这里,你将了解只批量写入的特定数据库。你将发现,这些数据库比传统数据库更简单,它们具有出色的性能,并具备可操作性、稳健性等特性。

第12~17章集中阐述速度层,该层弥补了批处理层的高延迟,为所有查询提供最新结果。在这里,你将了解NoSQL数据库、流处理和管理增量计算的复杂性。

第18章再次复习Lambda架构的相关知识,并进行查漏补缺。你将了解增量批处理、基本Lambda架构的变种,以及如何充分利用资源。代码下载和约定

本书的源代码可以在https://github.com/Big-Data-Manning找到。我们提供了运行示例SuperWebAnalytics.com的源代码。

大量源代码以代码清单的形式给出。这些代码清单提供了完整的代码段。一些代码带有注释,以着重强调或解释某部分代码。在正文的其他地方,代码片段会在必要时使用。Courier字体用来表示Java代码。在代码中,我们用粗体字来帮助你识别文本中的关键部分。作者在线

购买本书的读者将可免费使用由Manning出版社运营的私人网络论坛。你可以在论坛上评论本书、提出技术问题,并从作者和其他用户处得到帮助。要访问和订阅该论坛,请在Web浏览器输入“www.manning.com/BigData”。在论坛上注册后,你可在作者在线(AO)页面查看如下信息:注册后如何登录论坛、哪些帮助可用以及论坛上的行为规则。

Manning承诺为读者提供一个场所,以供个体读者之间、读者和作者之间进行有意义的对话。这并不是作者承诺进行任何特定数量的分享,作者对AO论坛的贡献是自愿的(无报酬的)。我们建议你尝试问作者一些有挑战性的问题,以免他们觉得了然无趣!

只要本书已出版,AO论坛和以前讨论的归档文件即可以从发行商的网站访问。关于封面插图

本书的封面插图是“Le Raccommodeur de Fiance”,意思是泥瓦匠。泥瓦匠擅长修补破损或有缺口的盆、盘、杯和碗,他走过法国的城镇和村庄,做着自己的生意。

该图摘自19世纪法国出版的Sylvain Maréchal四卷本的地域服饰风俗纲要。其中每幅图都是精心绘制并手工着色的。Maréchal丰富多彩的收藏向人们生动地展示了200年前城市和地区在文化上的差异——相互隔离,人们操着不同的方言和语言。在街头或是在农村,只是通过他们的着装就能够很容易地辨认他们生活的地方和他们的职业或生活中的地位。

自那时以来,着装规范已经发生改变,地区的多样性那时候是如此丰富,但这种多样性现在却消失了。现在很难分辨不同大陆的居民,更别说不同城市或地区的居民了。也许我们是用更多样化的个人生活取代了文化的多样性——现在肯定是更多样化和快节奏的科技化生活。

为了与其他计算书籍区别开来,Manning希望通过这样的封面插图来庆祝计算机业务的创造性和主动性,所以又将Maréchal的图片带回大众视野。致谢

如果没有周围很多人的帮助,这本书是无法完成的。我必须首先感谢我的父母,他们一直给我灌输热爱学习和探索世界的思想,并一直鼓励我在职业生涯中孜孜不倦地追求。

我的哥哥Iorav也一直在学术兴趣上给予我鼓励。我还记得,在我读小学时,他就教我学习代数。我第一次接触编程也是他介绍的——他教我Visual Basic,因为他在高中学过该课程。这些课程引发了我对编程的热情,引导我走上了职业道路。

非常感谢Michael Montano和Christopher Golda——BackType的创始人。从他们把我作为他们的第一个员工开始,我就拥有可以自己做决定的极度自由。这种自由对我探索和最大限度地利用Lambda架构来说至关重要。他们从未质疑过开源的价值,并允许我自由地开源我们的技术。深入地参与开源已经成为我的特权之一。

特别感谢我在斯坦福学习时遇到的很多教授。Tim Roughgarden是我见过的最好的老师——他从根本上提高了我严格分析、解构和解决困难问题的能力。尽可能多地上他的课是我一生中做出的最好决定之一。也感谢Monica Lam给我灌输了对Datalog优雅性的欣赏。许多年后我将Datalog与MapReduce结合,生成了平生第一个意义重大的开源项目——Cascalog。

Chris Wensel是第一个给我展示大规模的数据处理也有可能优雅和高效的人。他的Cascading库改变了我看待大数据处理的方式。

如果没有大数据领域的先驱者,我的工作是不可能实现的。特别感谢最早的MapReduce论文的作者Jeffrey Dean和Sanjay Ghemawat。感谢最初的Dynamo论文的作者Giuseppe DeCandia、Deniz Hastorun、Madan Jampani、Gunavardhan Kakulapati、Avinash Lakshman、Alex Pilchin、Swaminathan Sivasubramanian、Peter Vosshall和Werner Vogels。感谢Apache Hadoop项目的创始人Michael Cafarella和Doug Cutting。

在我的编程生涯中,Rich Hickey一直是给我最多灵感的人之一。Clojure是我至今用过的最好的语言,通过学习它,我成了一名更好的程序员。我很欣赏它的实用性和专注于简单性的特性。Rich在编程的状态和复杂性理念方面已经深深地影响了我。

开始写这本书时,我几乎称不上是作者。Renae Gregoire,Manning公司负责本书的策划编辑之一,特别感谢她帮助我提高写作技巧。她让我认识到使用例子来引入通用概念的重要性。在如何有效地组织技术写作方面,她给了我很多灵感。她教给我的技巧不仅适用于写作技术书籍,还适用于写博客、演讲和平时的沟通。因为掌握了一项重要的生活技能,所以我对她永远心存感激。

如果没有我的合著者James Warren的努力,本书将不会有现在的质量。为了让读者能够理解理论概念,并找到展示这些理论的更好方式,他做了大量的工作。本书之所以能够如此明晰,大部分是源于他强大的沟通技巧。

与我的出版商Manning合作是一种乐趣。他们的员工对我很耐心,并且理解寻找合适的方式写出这样一个大的话题是需要时间的。在整个过程中,他们都很支持我并帮助我,他们总是提供给我成功所需的资源。感谢Marjan Bace和Michael Stephens的支持,还有所有其他员工的帮助和全程指导。

我尝试尽可能多地学习其他作家的写作风格。Bradford Cross、Clayton Christensen、Paul Graham、Carl Sagan和Derek Sivers对我的影响很大。

最后,十分感谢对本书提出意见、评论和反馈的数百余名读者。这些反馈促成了本书的多次修改、重写和重组架构,直到我们找到有效地展示材料的方法。在此特别感谢Aaron Colcord、Aaron Crow、Alex Holmes、Arun Jacob、Asif Jan、Ayon Sinha、Bill Graham、Charles Brophy、David Beckwith、Derrick Burns、Douglas Duncan、Hugo Garza、Jason Courcoux、Jonathan Esterhazy、Karl Kuntz、Kevin Martin、Leo Polovets、Mark Fisher、Massimo Ilario、Michael Fogus、Michael G.Noll、Patrick Dennis、Pedro Ferrera Bertran、Philipp Janert、Rodrigo Abreu、Rudy Bonefas、Sam Ritchie、Siva Kalagarla、Soren Macbeth、Timothy Chklovski、Walid Farid和Zhenhua Guo。Nathan Marz

在回想为本书做出贡献的人时,我很震惊这么多人帮助过我。虽然不能一一列举,但这并不能减轻我的谢意。尽管如此,我还是希望向一些人明确表达我的感激之情:

·我的妻子Wen-Ying Feng——感谢你的爱、鼓励和支持,不仅是这本书,还有我们共同完成的一切。

·我的父母James和Gretta Warren——感谢你们带给我的无穷信仰,还有为我提供每个机会所做出的牺牲。

·我的姐姐Julia Warren-Ulanch——感谢你树立了一个光辉的榜样,使我可以跟随你的脚步。

·我的两位导师Ellen Toby和Sue Geller教授——感谢你们悉心回答我的每个问题,并教导我学习的乐趣不仅源于获得知识,更在于分享知识。

·Chuck Lam——感谢你很多年前对我说:“嘿,你听说过一个叫Hadoop的东西吗?“

·我的朋友和RockYou!、Storm8、Bina的同事——感谢我们一起共享的经历和把理论运用到实践的机会。

·Marjan Bace、Michael Stephens、Jennifer Stout、Renae Gregoire和整个Manning的编辑出版人员——感谢你们在本书出版过程中的指导和耐心。

·本书的评论者和早期读者——感谢你们的评论和批评,推动我们更加清晰地阐述理论;感谢你们让这本书变得更好。

最后,我要对Nathan表达最真挚的谢意,感谢你邀请我一起完成本书。在加入这个项目之前,我已经非常仰慕你的工作,共事时因为你的想法和理念,使我更加尊重你。这是一项莫大的荣誉和特权。James Warren第1章 大数据的新范式

本章内容

·扩展传统数据库时遇到的典型问题

·为什么NoSQL不是万能的

·从基本原理思考大数据系统

·大数据工具的情形

·SuperWebAnalytics.com的介绍

在过去的十年里,人们创造的数据量一路飙升——每秒产生超过30000GB的数据,并且数据创造的速度仍在加快。

我们处理的数据是多样化的。用户创建的内容有博客文章、微博、社交网络的互动和照片等。服务器不断地记录用户正在做什么的消息。科学家们创造了精确“测量”世界的标尺——周围世界的详细测量结果。互联网,这使得数据的最终来源是浩瀚无垠的。

数据的惊人增长已经深刻地影响了商业。传统的数据库系统,比如关系型数据库,已经被发展到极限。在越来越多的情况下,这些系统已经承受不住“大数据”的压力了。传统系统以及与它们相关的数据管理技术未能成功扩展到大数据的范畴。

为了应对大数据的挑战,新一代的技术出现了。其中许多新技术被分类到NoSQL术语下。在某些方面,这些新技术比传统数据库更复杂,而在另外一些方面,它们更简单一些。这些系统可以扩展到非常大的数据集,但要想有效地使用这些技术,需要一套全新的技巧——然而并没有放之四海而皆准的解决方案。

这些大数据系统中有许多是由Google所开创的,包括分布式文件系统、MapReduce计算框架和分布式锁服务。该领域中的另一个先驱是Amazon,它创造了一个革新性的分布式键/值存储系统,称为“Dynamo”。这些年,开源社区开发出了如下的Hadoop、HBase、MongoDB、Cassandra、RabbitMQ和无数其他项目。

本书是关于复杂性和可扩展性的。为了应对大数据的挑战,我们将从头开始重新考虑数据系统。你会发现在例如关系型数据库管理系统(Relational Database Management Systems,RDBMS)这样的传统系统中,人们管理数据的一些最基本方法对于大数据系统来说过于复杂。为简单起见,替代方法就是你即将探索的大数据新范式。我们把这种方法称为“Lambda架构”。

在第1章中,你将探索“大数据问题”以及为什么需要大数据的新范式。你会看到一些传统技术扩展的风险,并发现用传统方式构建数据系统的缺陷。以数据系统基本原理为出发点,我们将制订一种不同的方式来构建数据系统,以避免传统技术的复杂性。你会看到技术的最近趋势是如何促进新系统的使用的,最后你还会看到一个大数据系统的例子,我们将在整本书中贯穿构建这个大数据系统,以此演示关键概念。1.1 本书是如何组织的

你可以认为本书主要是一本理论性的书籍,专注于如何构建适用于任何大数据问题的解决方案。无论目前情形下的工具怎样,你将学习的原则都是有效的,你可以据此来严格选择适合你的应用程序的工具。

本书不是对数据库、计算和其他相关技术的调研。尽管在本书中,你将学习如何使用诸如Hadoop、Cassandra、Storm和Thrift等工具,但是本书的目的不是学会使用这些工具。相反,这些工具是学习构建具备鲁棒性和可扩展性的数据系统的一种手段。在这些工具之间进行相关的比较和对比,对你来说是不公平的,这样就会偏离学习底层原理的道路。换句话说,你将学习如何钓鱼,而不仅仅是如何使用特定的鱼竿。

通过这种方式,我们将本书划分为理论章节和例证章节。你可以只阅读理论章节,以全面理解构建大数据系统的方法——但我们认为,将理论映射到例证章节中的特定工具这一过程,将会使你对这些资料有更丰富、更细致的理解。

但是不要被本书的名字所欺骗——理论章节也有很多示例。本书中的首要案例是SuperWebAnalytics.com,这一案例在理论和例证章节都有用到。在理论章节,你可以看到SuperWebAnalytics.com的算法、索引设计和架构。例证章节利用具体的工具将这些设计映射为功能代码。1.2 扩展传统数据库

我们将从许多开发人员的“起点”开始大数据的探索,直击传统数据库技术的局限性。

假设上司的要求是构建一个简单的网络分析应用程序。这个应用程序能追踪客户期望追踪的任何统一资源定位符(Uniform Resource Locator,URL)的页面浏览量。每接收到一次页面浏览,客户的网页就用其URL ping应用程序的Web服务器。此外,应用程序在任何时候都能根据页面浏览量给出前100排名的URL。

首先启动一个如图1-1所示的页面浏览量的传统关系型模式。其后端包括一个该模式的表的RDBMS和一个Web服务器。每当有人加载被应用程序追踪的网页时,这一网页带着页面浏览ping Web服务器,同时Web服务器会在数据库中增加相应的行。图1-1 简单分析应用程序的关系型模式

让我们看看在完善应用程序时出现了什么问题。——如你所见,这里会遇到可扩展性和复杂性问题。1.2.1 用队列扩展

网络分析产品获得了巨大的成功,应用程序的流量正像野火一样增长。例如,公司举办了一个盛大的派对,但庆祝时,你开始从监控系统收到大量的电子邮件。这些邮件都在说同样的事情——“插入数据库时发生超时错误。”

你查看了日志,问题很明显——数据库跟不上负载,导致增加页面浏览量的写请求超时。

你需要尽快做些事情来解决这个问题。你会意识到每次只执行一次增量操作到数据库是很浪费的。如果你在单个请求中批处理多个增量操作,这样就可以更有效。所以你重构后端,使这一切成为可能。

所谓重构后端,不是让Web服务器直接访问数据库,而是在Web服务器和数据库之间插入一个队列。当你收到一个新的页面浏览后,该事件被添加到队列中;然后创建一个一次从队列中读取100个事件的工作进程,并在单个数据库更新操作中批量插入它们,如图1-2所示。图1-2 使用队列和工作进程实现批量更新

这个方案执行得很好,它解决了超时问题。它甚至还有额外的好处——如果数据库再次超载,只会使队列变得更大,而不会导致Web服务器超时和潜在的数据丢失。1.2.2 通过数据库分片进行扩展

不幸的是,添加队列并做批量更新只是可扩展性问题的一个“创可贴”。随着应用程序日渐受欢迎,数据库会再次超载。现有的工作进程跟不上写操作的速度,所以你尝试添加更多的工作进程来并行化更新。不幸的是,这并未起到多大的作用——显然,数据库是瓶颈。

使用Google搜索如何扩展写操作频繁的关系型数据库,你会发现最好的方法是使用多个数据库服务器,并在所有服务器上分散该表,使每个服务器拥有该表所包含数据的一个子集。这种方式被称为水平分区或分片。这种技术通过多个机器分散写操作的负载。

这里使用的分片技术是通过分片的数量对键的散列值进行取模,以此为每个键选择分片。使用散列函数将键映射到分片,可以使键均匀分布到所有分片。接下来写一个脚本来映射单个数据库实例中的所有行,并把数据分割成四个分片。这个脚本需要一段时间来运行,所以你要关掉增加页面浏览的工作进程,以便让该脚本完成运行,否则在转换时会丢失页面浏览的增量。

最后,所有应用程序代码需要“知道”如何为每个键找到分片。这就需要将用于从配置文件中读取分片数量的数据库处理代码封装成一个库,并且重新部署所有应用程序代码。你必须修改前100个URL的查询,从每个分片中获取前100个URL并将它们合并,用来获得全局的前100个URL。

随着应用程序变得越来越流行,用户不得不将数据库重新切分成更多的分片,才能跟得上写操作的负载。每次重新分片会让你觉得越来越痛苦——因为有很多工作进程需要协调,而且不能只运行一个脚本进行重新分片,那样速度会很慢。你必须并行地重新分片,并且同时管理大量的活跃工作进程的脚本。如果开发者忘记更新应用程序代码与新的分片数量,则会导致大量的增量操作写入错误的分片,因此必须编写一次性脚本来手动检查数据和移动任何错位的数据。1.2.3 开始处理容错问题

最终,系统中有如此多的分片,以至于其中一台数据库机器的硬盘频出故障。当计算机宕机时,这部分数据是不可用的。解决这个问题的方法如下:

·更新队列或工作进程系统,将不可用分片的增量操作放入一个单独的“等待”队列,且试图每5min刷新一次“等待”队列。

·使用数据库的复制功能为每个分片添加一个从分片,所以这样就会有一个备份以防主分片出现故障。虽然客户不在从分片中执行写操作,但至少还可以在应用程序中查看状态。

开发者会有这样的想法:“早期我花费时间为客户构建新功能,现在看来我只有花费所有时间来处理读写数据的问题了。”1.2.4 损坏问题

当运行队列或工作进程代码时,开发者在生产环境中不小心为每个URL部署了一个错误的网页浏览量的增量(为2,而不是1),直到24h后才意识到这个错误,但已经造成了损坏。因为无法知道哪些数据被损坏,所以每周的备份起不到帮助作用。虽然这种方式试图使系统具备可扩展性和对机器故障的可容忍性,但无法使系统具备对人为错误的应对方式。无论你怎样努力试图阻止错误的产生,它都将不可避免地在生产环境中出现。1.2.5 到底是哪里出错了

随着简单网络分析应用程序的发展,系统不断变得越来越复杂:队列、分片、副本、重新切分的脚本等。在数据上开发应用程序,不仅需要知道数据库模式,还需要知道更多的东西。代码需要知道如何与正确的分片通信,如果出错,从错误的分片进行读或写操作将是不可避免的。

还有一个问题是,数据库对它的分布式特性不是自我感知的,因此它不能帮客户处理分片、副本和分布式查询。这种复杂性在操作数据库和开发应用程序代码的部分中都会存在。

但最严重的问题是,系统并不是针对人为错误设计的。恰恰相反,实际上,随着系统变得越来越复杂,它越来越有可能产生错误。软件中的错误是不可避免的,如果不是在研发时犯错,则可能是写了任意破坏数据的脚本。备份无疑是不够的,开发者必须仔细考虑系统的设计方法,以降低人为错误可能导致的损害。容忍人为错误不是随意的,尤其在大数据给构建应用程序添加那么多复杂性时它更加必要。1.2.6 大数据技术是如何起到帮助作用的

我们所要学习的大数据技术,将以令人瞩目的方式解决这些可扩展性和复杂性问题。首先,为大数据所使用的数据库和计算系统是可以感知到自己的分布式特性的,所以可以帮助客户处理诸如分片和备份这些事情。用户不会遇到不小心查询了错误的分片的场景,因为这种逻辑是内化在数据库中的。扩展时,用户只需添加节点即可,系统将会自动重新调整到新的节点。

我们将了解的另一个核心技术是“使数据不可变”,即不是存储页面浏览量作为核心数据集,而是要存储原始网页浏览信息。因为随着新页面浏览的传入,存储页面浏览量需要用户不断地改变数据集。而原始网页浏览信息是从来不会改变的。所以当出现错误时,你可能会写入损坏的数据,但至少不会破坏良好的数据。基于数据变化方面,这是一个比传统系统更为强大的容忍人为错误的保证。对于传统的数据库,用户应谨慎使用不可变数据,因为这样的数据集将以很快的速度增长。但由于大数据技术可以扩展到如此多的数据,开发者就可以用不同的方式设计系统。1.3 NoSQL不是万能的

过去的十年,可扩展数据系统已经取得了大量的创新,其中包括如Hadoop这样的大规模计算系统,如Cassandra和Riak这样的数据库。这些系统可以处理大量的数据,但是需要认真的权衡。

比如Hadoop可以在非常大量的数据上并行化大规模批量计算,但计算具有较高的延迟。对于任何需要低延迟结果的计算,Hadoop是不适用的。

又如Cassandra这样的NoSQL数据库,通过提供一个比SQL中使用得更有限的数据模型,来实现可扩展性。压缩应用程序到这些有限的数据模型中是非常复杂的,且因为数据库是可变的,所以它们不能容忍人为错误。

这些工具单独使用时并不是万能的。但如果智能地结合使用,那么就可以生成能处理容忍人为错误和最低复杂性的任意数据问题的可扩展系统。这就是本书中将介绍的Lambda架构。1.4 基本原理

为了找到正确构建数据系统的方法,你必须先了解基本原理。那么,从最基本的层面上来说,数据系统是做什么的呢?

下面以一个直观的定义切入正题——数据系统基于过去到现在的信息来回答问题。例如,社交网络资料回答诸如“这个人的名字是什么?”“这个人有多少朋友?”这样的问题;银行账户网页回答诸如“我的当前余额是多少?”“最近我的账户发生了什么交易?”这样的问题。

数据系统不只是记录和重现信息。它们把零碎的信息结合起来生成答案。例如,银行账户余额是结合该账户上的所有交易信息得到的。

另一个重要的观察是:并非所有信息都是平等的,一些信息来自于其他信息。例如,银行账户余额源自历史交易,朋友数源自朋友列表,朋友列表源自用户资料中添加和删除朋友的总次数。

当你一直追踪信息的来源时,最终得到的是并非派生自任何事物的信息。这是最原始的信息,也就是说,你掌握的信息是真实的,只是因为它是存在的。这样的信息就被称为数据。

你也许对数据这个词有着不同的理解。通常数据与信息这个词是可以互换使用的。但在本书的剩余部分,在使用数据这个词时,所指的是一切派生得到的特殊信息。

如果一个数据系统通过查看过去的数据来回答问题,那么最通用的数据系统通过查看整个数据集来回答问题,所以数据系统最通用的定义如下:

query=function(all data)

换言之,任何所能想象的数据处理都可以表示为一个函数,该函数以接收到的所有数据作为输入。请记住这个等式,因为它是你将学到的所有知识的关键。后文将反复提及这个等式。

Lambda架构提供了一种通用的方法来实现任意数据集上的任意函数,并且让这个函数低延迟地返回结果。这并不意味着每次实现一个数据系统时,你永远使用完全相同的技术。你使用的具体技术可能基于自身需求的改变而改变。但是Lambda架构定义了一个一致性的方法来选择这些技术,并将它们连接在一起以满足你的需求。

下面讨论数据系统必须呈现出的属性。1.5 大数据系统应有的属性

你应该使大数据系统努力具备尽可能多的关于复杂性和可扩展性的属性。大数据系统不仅要运行良好、有效地使用资源,还必须易于推理。下面逐一介绍这些属性。1.5.1 鲁棒性和容错性

面对分布式系统的挑战,构建“做正确的事”的系统并非易事。尽管会遇到机器随机出现故障、分布式数据库中一致性的复杂语义、重复的数据、并发性等问题,但系统仍需能够正确运行。这些挑战使得“推断系统在做什么”变得很难。使大数据系统具备鲁棒性的一部分工作是为了避免这些复杂性(挑战),以便你能很容易推断系统(即探索系统)。

正如之前所讨论的,系统必须是可以容忍人为错误的。这是系统中经常被忽视的属性,开发者应予以重视。在生产系统中,偶尔有人出差错是不可避免的,比如部署错误代码损坏了数据库中的值。如果你将不变性和重新计算性构建到大数据的核心系统中,那么该系统通过提供一个清晰、简单的恢复机制,就能很容易地适应人为错误。这些内容将在第2~7章中详细描述。1.5.2 低延迟读取和更新

一方面,绝大多数的应用程序需要对读取操作有很低的延迟——这一时间通常是几毫秒到几百毫秒;另一方面,更新延迟的需求在不同应用程序之间有着很大的区别。一些应用程序需要更新操作立即传播,但其他应用程序延迟几个小时也是可以的。无论如何,大数据系统中如果需要低延迟更新,你就必须能够实现它。更重要的是,你需要在不影响系统鲁棒性的前提下,能够实现低延迟读取和更新。本书将从第12章开始介绍如何在速度层实现低延迟更新。1.5.3 可扩展性

面对数据或负载的增加,可扩展性能够通过将资源添加到系统中来保持性能。Lambda架构在系统堆栈的所有层是水平可扩展的——扩展是通过添加更多的机器来实现的。1.5.4 通用性

一个通用系统可以支持大多数应用程序。事实上,如果本书所述内容没有推及大多数的应用程序,那么它不会是非常有用的。因为Lambda架构是基于所有数据功能的,可以推广到所有应用程序,无论是财务管理系统、社交媒体分析、科学应用、社交网络,还是其他应用。1.5.5 延展性

每次添加相关功能或对系统的工作方式做出一些改变时,你不需要重复劳动。具备可延展性的系统允许以最小的开发成本添加功能。

通常,一个新特性或一个现有功能的改变需要将旧数据迁移成新格式。系统具备可延展性的要素之一就是它容易实现大规模迁移。能够轻松、快捷地完成大规模迁移,是你将学习的方法的核心。1.5.6 即席查询

几乎每个大型数据集中都有意料之外的值,因此,能够对数据进行即席(ad hoc)查询是非常重要的。此外,能够任意地挖掘数据集为业务优化和新的应用程序也提供了可能。最终,除非你可以问这些数据任意问题,否则你将不能利用数据发现有趣的事情。本书的第6章和第7章在讨论批处理时,将介绍进行即席查询的方法。1.5.7 最少维护

维护是开发人员的重负,它是需要保持系统平稳运行的工作。包括预测什么时候添加用于扩展的机器、保持进程启动并运行以及调试生产环境中的任何错误。

减少维护的一个重要途径是选择实现复杂性尽量低的组件,即依赖底层具有简单机制的组件。特别是分布式数据库,它往往有着非常复杂的内部结构。系统越复杂,出错的可能性就越大,你越需要了解有关系统调试和优化的知识。

通过依靠简单的算法和组件,来降低实现复杂性,进而实现最少维护。Lambda架构采用的一个技巧是:将复杂性推出核心组件,并将其送到几小时后输出是可丢弃的系统的片段中。所使用的最复杂的组件,如读/写分布式数据库,在输出最终是可丢弃的这一层中。本书第12章在讨论速度层时将详细讨论该技术。1.5.8 可调试性

一旦出错,大数据系统必须提供必要的信息来调试系统。关键是能够追踪系统中的每一个值,并能明确该值是如何产生的。“可调试性”是在Lambda架构中通过批处理层的功能特性和尽可能使用重新计算算法来实现的。

将所有这些属性组合在同一个系统中实现,这看起来是一个令人生畏的挑战。但从基本原理出发,正如Lambda架构做的,这些属性将从最终的系统设计中自然而然地生成。

在深入介绍Lambda架构之前,我们来看更传统的架构——以依赖增量计算为特征——以及它们无法满足这些属性的原因。1.6 全增量架构的问题

在最高的层次上,传统的架构如图1-3所示。这种架构的特征是读/写数据库的使用以及随着新数据的可用,增量地维护这些数据库中数据的状态。例如,一个计算页面浏览量的增量方法,将通过对URL计数器加1来处理新的页面浏览量。这种架构的特征比关系型与非关系型更基础——事实上,几十年来,绝大多数的关系型和非关系型数据库都是用全增量架构来部署的。图1-3 全增量架构

值得强调的是,全增量架构应用得如此广泛,以至于许多人没有意识到可以使用另外一种不同的架构来避免它们的问题。这是熟悉的复杂性的典型示例——复杂性如此根深蒂固,以至于人们认为无法避免它。

研究全增量架构的问题意义重大。通过查看任何全增量架构带来的一般复杂性,我们将开始这个主题的探索。然后,我们将查看针对相同问题的两种截然不同的解决方案:一种是使用最好的全增量解决方案;另一种是使用Lambda架构的解决方案。你会发现,全增量的版本在各方面明显更加糟糕。1.6.1 操作复杂性

在全增量架构中有许多内在复杂性,给操作生产基础架构造成了困难。这里我们将关注其中一个方面——需要读/写数据库来执行在线合并,以及为保证这项工作平稳运行所必须做的操作。

在读/写数据库中,随着磁盘索引会逐步增加和修改,但部分索引从未使用过。这些未使用的部分索引占用空间,最终需要被回收以防磁盘被填满。如果索引一旦变成未使用的,回收空间就立刻回收,那样付出的代价太高,所以空间在一个被称为合并的进程中不定期地被批量予以回收。

合并是一类集中操作。在合并过程中,服务器对CPU和磁盘有更高的要求,这大大降低了该时期机器的性能。众所周知,数据库(如HBase和Cassandra)都需要仔细地配置和管理,以免在合并时出现问题或服务器锁定。合并时性能的损失是一类甚至会导致级联故障的复杂性——如果太多机器同时执行合并操作,那么它们所支撑的负载将必须由集群中的其他机器处理。这可能使剩余的集群过载,导致彻底失败。这种失败已经出现过很多次了。

为了正确地管理合并,你必须在每个节点上都安排合并,以保证同一时间不会有太多节点受到影响;你必须知道一个合并操作会花多少时间以及时间的方差,以免有比预期更多的节点在执行合并;你必须确保节点上有足够的磁盘容量,以保证在合并期间它们能够维持正常的操作;你还必须确保集群有足够的容量,以保证在合并时资源丢失就不会造成过载。

所有这些都可以由一个合格的操作人员来管理,但我们的观点是,处理任何一种复杂性的最好方式是完全摆脱这种复杂性。系统失败模式越少,就越不可能遇到意外的故障时间。处理在线合并是全增量架构中固有的复杂性,但在Lambda架构中,主数据库不需要任何在线合并。1.6.2 实现最终一致性的极端复杂性

试图让系统高可用会导致增量架构的复杂性。高可用系统甚至允许在机器或部分网络发生故障时进行查询和更新。

事实证明,实现高可用性将与另一个称为一致性的重要属性直接竞争。具备一致性的系统返回结果时,会考虑以前所有的写操作。CAP(Consistency、Availability、Partition tolerance)原理表明,在网络分区情况下,不可能在同一系统中同时实现高可用性和一致性,所以在一个网络分区中,高可用系统有时会返回陈旧的结果。

本书将在第12章深入讨论CAP原理——现在我们总是希望专注于不能实现的完全一致性和高可用性上,这会影响构建系统的能力。事实证明,如果业务需求中对高可用性的需要超过完全一致性,那么你必须处理大量的复杂性。

一旦网络分区结束,为了高可用性系统能回到一致性状态(即最终一致性),应用程序需要许多帮助,例如,在数据库中维护一个计数的基本用例,最显而易见的方法是在数据库中存储一个数值,当接收到需要加和的事件时,就做增量。你也许会很惊讶地发现,如果采取这种方法,在网络分区时,你会遇到海量数据丢失的情况。

导致这种情况的原因是,分布式数据库通过保存所有被存储信息的多个副本来实现高可用性。当你保存了相同信息的多份副本时,即使机器出现故障或网络分区,这些信息仍然可用,如图1-4所示。在网络分区时,选择成为高可用性的系统,只要副本是可获得的,就会有客户端的更新。这将导致副本产生分歧并接收不同的更新。只有分区消失,副本才可以合并成一个共同的值。

当网络分区开始时,假设有两个副本的计数为10。假设第一个副本得到两个增量,第二个副本得到一个增量。当这些副本合并在一起时,值分别为12和11,合并后的值应该是多少?虽然正确的答案是13,但是没有办法通过查看数值12和11得到该值。这两个数可能在11的时候产生分歧(在这种情况下,答案会是12),或者它们可能会在0的时候产生分歧(在这种情况下,答案是23)。图1-4 使用副本增加可用性

为了做高可用性的、正确的计算,只存储一个计数是不够的。当值出现分歧时,你需要一个负责合并的数据结构,并且需要实现一段用于分区结束后对值进行修复的代码。为了维护一个简单的计数,你必须处理令人难以置信的复杂性。

一般来说,在增量、高可用性系统中处理最终一致性,是不直观的且容易出错的。在高可用、全增量系统中,这种复杂性是固有的。稍后你将看到Lambda架构本身是如何以一种不同的方式,极大地减少实现高可用性、最终一致性系统的负担的。1.6.3 缺乏容忍人为错误

我们希望指出的全增量架构的最后一个问题是,它们天生缺乏容忍人为错误的特性。增量系统不断修改保存在数据库中的状态,这意味着即使是一个错误也可以修改数据库中的状态。因为错误是不可避免的,所以全增量架构的数据库肯定会受到破坏。

重要的是要注意,全增量架构中,这是不用重新思考架构就可以解决的少数复杂性之一。下面考虑如图1-5所示的两种架构:同步架构,应用程序直接更新数据库;异步架构,在后台更新数据库之前事件先进到一个队列中。在这两种情况下,每个事件都被永久地记录到事件的数据存储中。通过保存每个事件,如果是人为错误导致数据库被破坏,那么你可以返回到事件存储,为数据库重建正确的状态。因为事件存储是不可变且不断增长的,冗余校验,如权限,可以放入事件存储,这使得不可能因出现某个错误而影响事件存储。这种技术也是Lambda架构的核心,我们将在第2章和第3章深入讨论。图1-5 为全增量架构添加记录

尽管附带日志记录的全增量架构可以解决无日志记录的完全增量架构对人为错误缺乏容忍的缺陷,但是日志记录对于前面讨论的其他复杂性于事无补。在下一节中你将看到,纯粹基于完全增量计算的各种架构,包括那些附带日志记录的架构,需要努力解决很多问题。1.6.4 全增量架构解决方案与Lambda架构解决方案

贯穿整本书实现的示例查询之一,适合作为全增量架构和Lambda架构的一个很好的对比。这个查询没有任何矫揉造作的地方——事实上,它是基于我们职业生涯中多次面临的现实生活中的问题的。这个查询用于处理网页浏览分析,并且完成对传入的两类数据的查询:

·页面访问数据,包括用户ID、URL和时间戳。

·等价数据,其中包含两个用户ID。一份等价数据表明两个用户ID是指同一个人。例如,你可能在电子邮箱sally@gmail.com和用户名sally之间有一份等价数据。如果sally@gmail.com也注册了用户名sally2,那么你在sally@gmail.com和sally2之间有一份等价数据。通过传递性,你会知道用户名sally和sally2指的是同一个人。

查询的目的是计算一段时间内对一个URL来说独立访客的数量。查询应该将所有这段时间的数据加起来,并以最小的延迟(少于100ms)来响应。查询的接口如下:

使得这个查询实现起来有些棘手的就是那些等价数据。如果在一个时间范围内,一个人用两个用户ID访问了相同的URL,通过等价数据的连接(甚至传递),这应该只算一次访问。一个新的等价数据的传入,可以改变任何URL在任何时间范围内任何查询的结果。

在这一点上,我们将不去展示解决方案的细节,因为必须提及非常多的概念(如索引、分布式数据库、批处理、HyperLogLog等)才能理解它们。此时让读者淹没在这些概念中往往会适得其反。相反,我们将专注于解决方案的特征和它们之间的显著差异。最佳完全增量解决方案将在第10章详细讨论,Lambda架构解决方案将在第8、9、14和15章中进行讨论。

两种解决方案可以在准确性、延迟和吞吐量三个轴上进行对比。Lambda架构解决方案在各方面表现得更胜一筹。这两种方案都必须实现近似值,但全增量版本被迫使用具有3~5倍甚至更糟糕的错误率的劣质近似技术。在全增量版本中执行查询的代价更高,并且会影响延迟和吞吐量。但这两种方案之间最显著的区别是:全增量版本需要使用特殊的硬件来实现接近合理的吞吐量。因为全增量版本必须做许多随机访问查找来解决查询问题,它实际上需要使用固态硬盘,以防磁盘寻道时间成为瓶颈。

Lambda架构可以生成在每个方面都具有更高性能的解决方案,同时也避免了困扰全增量架构的复杂性,展示了正在发生的非常根本的事情——关键是挣脱全增量计算的束缚,采用不同的技术。现在让我们看看Lambda架构是如何做到这一点的。1.7 Lambda架构

实时计算任意数据集上的任意函数,是一个令人望而却步的问题。没有单独的工具可以提供完整的解决方案,相反,你必须使用各种工具和技术构建一个完整的大数据系统。

Lambda架构的主要思想是将大数据系统建立为一系列的层,如图1-6所示。每一层满足属性的一个子集,且通过该层的下一层所提供的功能来构建。虽然你通过整本书来学习如何设计、实现和部署每一层,但整个系统是如何有机结合在一起的高层思想是很容易理解的。图1-6 Lambda 架构

一切从query=function(all data)等式开始。理想情况下,你可以不断运行这个函数来获取结果。不幸的是,即使这是可能的,也需要占用大量的资源,并且相当昂贵。想象一下,每次你想要响应某人当前位置的查询时,都必须要读取1PB的数据集。

最显而易见的替代方法是预先计算查询函数。我们将预先计算的查询函数称为批处理视图(Batch View)。这不是动态计算查询,而是从预先计算好的视图中读取结果。预先计算的视图是有索引的,因此可以用随机读取的方式进行访问。该系统看起来像是这样的:

在该系统中,你在所有数据上运行一个函数来获取批处理视图。然后,当你想知道一个查询的值时,只需针对批处理视图运行一个函数即可。批处理视图可以使你很快地从中获得所需要的值,而无须扫描所有数据。

这个讨论有点抽象,下面举例说明。假设你正在构建一个网络分析的应用程序(再次构建),并且想要查询任一范围天数内某个URL的页面浏览量。如果你在所有数据上运行该查询函数,最好扫描数据集中相应时间范围内那个URL的页面浏览量,并返回结果的计数。

批处理视图方法替代在所有页面浏览上运行一个函数的方式,来预先计算[url,day]键的索引,以获得某天某个URL页面浏览的计数。然后为了解决该查询,从视图中检索相应时间范围内的所有天的值,并将所有计数相加,以得到最终的结果。这种方法如图1-7所示。图1-7 批处理层的架构

应该明确的是,到目前为止所描述的这种方法似乎缺了点什么。创建批处理视图显然会是一个高延迟操作,因为它是在你的所有数据上运行一个函数。在批处理视图结束时,很多新的数据将被收集但是没有展示在批处理视图中,并且这个查询将过期许多小时。但是我们暂时忽略这个问题,因为它是可以解决的。假设过期几个小时的查询是可以的,那么我们继续探索通过在完整数据集上运行函数来预先计算批处理视图的这个想法。1.7.1 批处理层

Lambda架构中实现batch view=function(alldata)等式的这部分被称为批处理层。批处理层存储数据集的主副本,并在主数据集上预先计算批处理视图(见图1-8)。主数据集可以被视作一个非常大的记录列表。图1-8 批处理层

批处理层需要能够做两件事:存储不可变的、不断增长的主数据集;在该数据集上运行任意函数。最好使用批处理系统完成这种类型的处理。Hadoop是批处理系统的一个典型例子,我们将在本书中使用Hadoop来阐述批处理层的概念。

批处理层最简单的形式可以用如下的伪代码表示:

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载