构建一个完全面向服务的系统一直是Twitter追求的目标,而从 之前的文章中,我们分享过Twitter从容应对14.3万TPS峰值的整个系统概览,不过各个服务组件的细节却并未涉及。有幸的是,该公司近日披露了其自主研发的数据库系统Manhattan,在提供高可靠、高可用等特性的同时,Twitter仍然灌输了其面向服务的特性。
以下为译文:
随着Twitter成长为全球化的用户交流、表达平台,它的存储需求也在一直增大。在过去的几年中,我们发现我们急需一个可以进行每秒数百万次查询,在实时环境中延迟低的存储系统。可用性和速度成为至关重要的因素。理想的系统不仅需要速度快,还需要可扩展到全球多个地区。
在过去的几年中,我们为许多开源数据库做出了重要的贡献。但是我们,Twitter的实时特性导致当下任何开源系统都无法满足其低延迟的需求。我们花费了大量时间来满足不同产品的需求,提供新的存储容量,耗费人力、流程以满足使用需求。但是依照我们在Twitter规模下的开发运行生产存储经验,这种状态是不可持续的。所有我们试图构建下一代Twitter分布式系统——我们称之为Manhattan 。Manhattan 不但需要满足现有需求,还需要迎合未来潜在的需求。
Twitter存储系统全览
如今众多数据库有着众多特性,不过根据我们的经验我们可以明确几个需求,这些需求符合我们的未来预期,足以应对我们将面对的大部分情况并解决实时使用中可能遇到的问题——如正确性、可操作性、可见性、性能和客户支持等。我们的要求如下:
1. 可靠性:Twitter服务需要一个耐用的数据存储,这个数据存储的性能必须是可估的。我们要求在各种失败、减速、扩张、热点或者在其他我们遇到情况下,这个数据存储都是可信任的。
2. 可用性:我们大多数用例都推崇可用一致性,因此我们需要一个不间断的最终实现一致性的数据库。
3. 可扩展性:我们要求应用可以应对今后的变化需求,因此我们需要一个可靠的、模块化的基础,并在这个基础上构建从新存储引擎到强一致性的所有部件。此外,无结构键值数据模型最适合客户的需求,这个模型还给了客户今后添加结构空间。
4. 可操作性:随着集群从几百个节点变成几千个节点,即使是最简单的操作也会是对操作者耗时、痛苦的折磨。为了有效的使用人力资源,我们从一开始就优化操作。针对每个新特征,我们都要考虑操作便捷性,并对问题进行诊断。
5. 低延迟:作为实时服务,Twitter的产品需要一致的低延迟,所以我们需要作出一些权衡。
6. 生产环境的可扩展性:在分布式系统中,扩展性挑战无处不在。Twitter需要一个可扩展的数据库,而且这个数据库的每个指标今后都可以继续发展到新的高度——集群大小,每秒查询次数,数据大小,地理特性以及租户数量——在不牺牲成本效率、易于操作的前提下。
7. 开发人员生产率:公司的开发者应当可以存储任何用来构建服务的内容,存储过程基于自助服务平台,不需要来自存储工程师的干涉,存储基于一个“能工作”的系统。
8. 开发者应当可以将任何所需内容存储到一个可用的系统。
Twitter规模下的可靠性
当我们开始构建Manhattan 时,我们已经有许多Twitter的大存储集群,因此我们充分了解规模运行系统所带来的挑战,这些经验告诉我们新系统中该争取和避免哪些特性。
一个可靠的系统是一款在任何操作下都可信任的、运行良好的系统,这种可估性能是非常难实现的。可估系统的关键是对最糟状态的判断;而平均表现情况就没有那么重要了。在一个正确配置、良好实现的系统中,平均表现很少导致问题。当我们看公司指标时我们会看如p999和p9999延迟之类的指标,我们关心最慢的0.01%的请求到底有多慢。我们需要为最差的情况做好打算。例如,如果有一个周期性的批量工作每天都会有一个小时降低性能,那么可接受的稳定表现也就无从谈起了。
因为可估的优先级,我们需要在任何潜在问题、失败模式下都保持良好的表现。客户对我们的实施细节和各种借口并不感兴趣;我们的服务对他们、对Twitter要么是可用的,要么是不可用的。即使有些时候我们需要做出一些权衡以应对一些不太可能发生的状况,我们也需要这么做,我们必须要铭记——有些时候,罕见的事情也会发生。
如今规模不仅来自机器数量、请求数量、大规模数据,还来自人员规模——使用、支持系统的人数的增长。我们通过关注以下几个问题进行管理:
如果某个用户制造了问题,那么这个问题应该仅存在于这个用户,不会扩散。
我们和用户都应当可以很容易的分辨出问题是源于存储系统还是用户。
对于潜在的问题,一旦发现、诊断,我们就要最小化恢复系统所需的时间。
我们必须了解各种失败模式将如何呈现给用户。
完成常规的操作、诊断修复大部分问题,都不应该要求一个操作员有深入、全面的操作系统知识。
最终,我们依据规模操作的经验构建了Manhattan ,复杂性是我们最大的敌人。最终,简单、可行战胜了浮华。我们提倡简单,可靠工作的,具有良好的一致性,提供良好的能见性的系统;我们不提倡理论上完美,实际工作时不能正常运行或者可见度低、可操作性差、与其它核心需求不兼容的系统。
构建一个存储系统
在构建新一代存储系统时,我们决定对系统分层,以得到模块化、稳定的底层作为构建基础,之后在此之上更新功能将不需要做大调整。
以下是设计目标:
保持核心精益和简单
越早实现价值越高(专注于增量)
第一要义:多租户,QoS,自助服务
关注可估性
存储不只是一项技术,更是一个服务
层
我们将Manhattan 分为四层:接口,存储服务,存储引擎和内核
内核
内核是存储系统的关键部分:内核具有高稳定性、鲁棒性。内核负责处理失败,最终一致性,路由,拓扑管理,数据中心内复制,数据中心间复制和解决冲突。通过系统内核,关键部分结构实现完全可插拔,因此我们可以进行设计和改进的快速迭代,进行有效的单元测试。
操作者可以在任何时间改变拓扑以添加或删除容量,我们的可见性和强大拓扑管理是至关重要的。我们将拓扑结构信息存储到Zookeeper,尽管Zoopkeeper不是读写的关键路径,但是它有强大的协调能力,也是Twitter基础建设中的一个管理组件。我们也付出了大量的努力确保对内核最佳可见性,我们通过一组Ostrich指标来了解所有主机的正确性和表现。
一致性模型
很多Twitter应用都很好的兼容于最终一致模型。我们推崇覆盖几乎所有用例的一致性,因此我们要在内核中把Manhattan 建设为一个最终一致模型。然而,总会有应用程序需要强烈的自身数据一致性,建立这样一个系统高优先级是吸引更多的顾客。强一致性是可选的模型,开发者要做好权衡。在强一致性系统中,使用者将对于一部分分区拥有主控权。Twitter上的很多用例是不能忍受几秒钟的延迟的(因为失败后会被淘汰)。我们为开发人员提供良好的默认设置,并帮助他们理解两个模型之间的权衡。
实现一致性
为了在最终一致系统中实现一致性,开发者需要一个称之为副本和解的机制。这是一个增量机制,它一直运行可以和解副本数据的进程。它解决位衰减、系统bug、写丢失(节点宕机很长一段时间)和数据处理中心间的网络分区带来的问题。除了副本和解外,我们还可以使用;另外两个机制进行优化,以实现更快的收敛性:读修复(read-repair),这个机制基于读数据的速率,使得常被访问的数据更快收敛;暗示移交(hinted-handoff),基于节点不稳定或离线一段时间,是二次传递机制。
存储引擎
存储系统的最底层是数据如何存储在硬盘上,数据结构如何存储在内存中的。为了降低管理多个存储引擎的多个数据处理中心所带来的复杂性和风险,我们决定将最初的存储引擎设计在内部,如果有额外需求可以灵活的插入外部存储引擎。这使得我们关注最必要的部分,审核改动是否应当进行。我们现在有三个存储引擎:
seadb是只读文件格式,读来自Hadoop的批量处理数据。
sstable是基于重写工作负载格式的日志结构合并树。
btree,基于重读,轻量级写负载的格式。
所有存储引擎都支持基于块的压缩。
存储服务
我们在Manhattan 内核顶层创建了额外的服务,这些服务使得Manhattan 拥有更强的鲁棒性,这种特性也是开发者一直以来期待的。一些例子如下:
Hadoop批导入:Manhattan 的最初用例位于Hadoop生成的数据之上,作为其高效服务层。我们设计了一个进口管道,这个管道允许客户在HDFS的数据集生成为简单的格式,并通过自助服务接口指定文件位置。我们的观测者自动选择了一个新数据集,在HDFS中将其转为seadb文件,这样他们就可以将这些将数据导入集群中以获取来自SSD卡或内存的快速服务。我们致力于将这个管道流线化,使其简单、便捷,帮助开发者快速进行进化数据集的迭代。我们从用户了解到的一点是他们希望生成大的,大概几千兆的数据集,通常后续版本的数据变化小于10%~20%。为了减少网络带宽,我们采取优化措施——产生我们在下载数据到副本时可使用的二进制差别,进而大幅度减小数据中心的导入时间。
强一致性服务:强一致性服务保障用户在进行系列操作时拥有强一致性。我们使用一个一致算法搭配了一个复制日志来保证顺序事件顺利到达所有副本。这使得我们可以进行诸如检查设置(CAS)、强读、强写的操作。现在我们支持两种模式:LOCAL_CAS和GLOBAL_CAS。Globall CAS使得开发者在法定数据控制中心间获得强一致性,Local CAS使开发者在指定数据控制中心内获得强一致性。考虑到应用的延迟性和数据模型,两种操作各有利弊。
时间序列计数器服务:我们开发了一个特定的服务来处理Manhattan 中的大量时间序列计数器。推动这项需求的“客户”是我们的可观察的基础设施,它需要一个每秒可以处理成千上万个增量的系统。在这种规模下,我们的工程师通过种种实践,权衡诸如耐久性、增量对报警系统可见前的延迟、用户可以接受的次秒级交通模式等方面,最终得出解决方案。最终方案是在一个优化的Manhattan 集群上添加一个轻量的、高效的计算层,这大大满足了我们的要求、提高系统可靠性。
接口
接口层描述了用户如何与我们的存储系统交互。现在我们已经向用户开放一个键/值接口,我们也正在实现其它接口——如进行边缘交互的图接口。
工具
为了满足集群简易性的需求,我们需要花大功夫研究如何设计最好的日常操作的工具。我们希望系统处理尽可能复杂的操作,并希望通过高层语义的命令对操作者屏蔽这些复杂的实施细节。我们先实现的工具包括可以仅通过编辑宿主组织和权重文件就可以实现修改整个系统的拓扑结构的工具,可以通过单个命令行就可以重启所有节点的工具。当早期的工具变得太复杂时,我们建立一个自动化的代理,这个代理接受简单的命令作为集群的状态的目标,并且可以堆栈、结合,在没有操作者的关注下安全执行指令。
存储服务
我们对现有数据库的一个通常认识是这些数据库为了特定的用例所设置、构建、管理。随着Twitter内部服务的成长,我们认识到先前的认识无法满足商业需求。我们的解决方案是存储是一项服务。通过构建一个工程师完全可控的自服务存储系统,我们大大提高了工程团队和运营团队生产力。工程师可以提供他们的应用程序所需要的(存储大小、每秒查询等)和开始在几秒钟内准备存储,无需安装硬件或模式的设置。公司内部客户运行在多租户环境下,运营团队管理这个环境。集群管理自助服务和多租户带来了一定的挑战,所以我们把这个服务层作为一个头等特征:我们为客户提供客户数据和工作量的能见度;我们有内置的配额执行和限速,当工程师越过阈值时它们会提醒工程师;我们的信息由我们的容量和敏捷管理团队进行管理分析和汇报。通过方便工程师运行新功能,我们看到了新用例实验和增值的增长。为了更好的处理这些,我们开发了内部API来公布这些成本分析数据,这些分析帮助我们可以确定哪些用例需要花费的多,哪些不经常使用。
关注客户
尽管我们的客户都是Twitter的雇员,我们始终要提供优质服务,因为他们是我们的客户。我们需要做到随叫随到,进行应用程序间行为的隔离,在一切工作中都考虑用户体验。大部分开发人员都了解需要阅读服务的详细文档,但是对存储系统的功能增加或调整需要慎重考虑,而将特性无缝的集成到自服务中更需要处理多种不同的需求。当一个用户有问题时,我们需要设计服务,这样我们可以快速准确的定位根本原因,这包括当工程师访问数据库时来自不同客户和应用程序的问题和突发问题。我们已经很成功的将Manhattan 构建为一种服务,而不是只一项技术。
多租户和QoS
支持多租户——允许多个不同应用程序共享同一资源——这从一开始就是一个关键需求。Twitter先前使用的系统中,我们为每个特征构建外部集群。这增加了操作负担,浪费资源,并且阻碍了客户推出新功能的速度。如上文所说,允许多个用户使用同一组群将增强运行系统的竞争力。我们现在必须要考虑隔离性,资源管理,多个用户能力模型,速率限制,QoS以及配额等等。为了给客户提供所需的可视性,我们设计了自己的速率限制服务来增强用户对资源和配额的使用。如果需要,我们可以通过观测指标是否越过阈值来确保应用程序没有影响系统中的其它程序。
速率限制不是在粗粒度下进行,它被实现到亚秒级,并容忍现实世界中峰值。我们不仅要考虑自动执行,还要关注应该给操作者提供哪些手动控制操作符来解决问题,关注如何减轻对所有用户的负面影响。我们构建为每个客户提取数据、将数据发送给容量团队的API,容量团队要确保当客户有任何大小需求时(Twitter标准的),我们的资源都是可用的,这样那些工程师可以在不需要我们的额外的帮助的情况下开始工作。将这些所有内容都直接整合到子服务系统使得客户可以在大多租户集群中更快的启用新特性,并允许我们更容易的吸收峰值流量,因为大多数客户不使用他们所有的资源。
着眼未来
我们要做的还有很多。挑战在不断的增加,Manhattan 内置的特性也在迅速增加。促使自己做的更好、更聪明是我们内核存储团队的动力。我们以我们的价值为豪:我们所做的一切会让Twitter变得更好,那么我们如何才能让我们的客户更成功?我们打算发布一个涉及Manhattan 更多技术细节和运行Manhattan 两年经验的白文件,敬请期待!
原文链接:http://www.csdn.net/article/2014-04-08/2819197-manhattan-realtime-distributed-database-at-twitter-scale