当今许多新型应用都属于数据密集型,而不是计算密集型。对于这些类型应用,CPU的处理能力往往不是第一限制性因素,关键在于数据量、数据的复杂度以及数据的快速和多变性。
数据密集型应用系统设计也是基于标准模块构建而成的,通常包含以下模块:
当出现意外情况如硬件、软件故障、人为失误等,系统可以继续正常运转;虽然性能可能有所降低,但确保功能正确。
对于软件,典型的期望包括:
可能出错的事情成为错误或者故障,系统可应对错误则成为容错或者弹性。
注意,故障和失效不完全一致。故障通常被定义为组件偏离其正常规格,而失效意味着系统作为一个整体停止,无法向用户提供所需的服务。
在这种容错系统中,用于测试目的,可以故意提高故障发生概率,例如通过随机杀死某个进程,来确保系统仍保持健壮。Netflix的Chaos Monkey系统就是这种测试的典型例子。(混沌工程)
当我们考虑系统故障时,对于硬件故障总是很容易想到:硬盘崩溃,内存故障,电网停电,甚至有人误拔掉了网线。
我们的第一个反应通常是为硬件添加冗余来减少系统故障率。例如对磁盘配置RAID,服务器配置双电源,甚至热插拔CPU,数据中心添加备用电源、发电机等。
但是,随着数据量和应用计算需求的增加,更多的应用可以运行在大规模机器之上,随之而来的硬件故障率呈线性增长。因此,通过软件容错的方式来容忍多机失效成为新的手段,或者至少成为硬件容错的有利补充。这样的系统更具有便利性,例如可以用滚动升级的方式来给操作系统打补丁。
我们通常认为,硬件故障之间多是相互独立的。
另一类故障则是系统内的软件问题。这些故障实现更加难以预料,而且因为节点之间是有软件关联的,因为往往会导致更多的系统故障。
导致软件故障的bug通常会出于因而不发的状态,知道碰到特定的触发条件。
软件系统问题有时没有快速解决方法,而只能仔细考虑很多细节,包括认真检查依赖的假设条件与系统之间的交互,进行全面的测试,进程隔离,允许进程崩溃并自动重启,反复评估,监控并分析生产环节的行为表现等。
设计和构建软件系统总是由人类完成,也是由人来运维这些系统。即使有时意图是好的,但人却无法做到万无一失。我们假定人是不可靠的。
随着规模的增长,例如数据量、流量或复杂性,系统应以合理的方式来匹配这种增长。
可扩展性是用来描述系统应对负载增加能力的术语。它并不是衡量一个系统的一维指标,谈论“X是可扩展”或“Y不可扩展”没有太大的意义。讨论可靠性通常要考虑这类问题:“如果系统以某种方式增长,我们应对增长的措施有哪些”,“我们该如何添加计算资源来处理额外的负载”。
负载可以用成为负载参数的若干数字来描述。参数的最佳选择取决于系统的体系结构。它可能是Web服务器的每秒请求处理次数,数据库中写入的比例,聊天室的同事活动用户数量,缓存命中等。有时平均值很重要,有时系统瓶颈来自于少数峰值。
在批处理系统如Hadoop中,我们通常关心吞吐量,或者在某指定数据集上运行作业所需的时间;而在线系统通常更看重服务的响应时间。
我们经常考察服务请求的平均响应时间。然而,如果想知道更典型的响应时间,平均值并不是合适的指标。最好使用百分位数,将收集到的响应时间按从快到慢排序,取前百分之X的数据,得到X百分位数,也叫pX(pctX),如p95(pct95)。
应对负载增加的常用方法有垂直扩展(升级到更强大的机器)和水平扩展(将负载分布到多个更小的机器)。
随着时间的推移,许多新的人员参与到系统开发和运维,以维护现有功能或适配新场景等,系统都应高效运转。
垂直扩展通常更简单,但高端机器可能非常昂贵,且存在上限,往往还是无法避免水平扩展。
把无状态服务扩展至多态机器相对比较容易,而有状态服务于的复杂性会大大增加。出于这个原因,通常的做法一直是,将数据库运行在一个节点上(采用垂直扩展策略),知道高扩展或高可用性的要求迫使不得不做水平扩展。
然而,随着相关分布式系统专门组件和变成接口越来越好,至少对于某些应用类型来说,上述通常做法或许会改变。可以乐观的设想,即使应用可能并不会处理大量数据或流量,但未来分布式数据系统将成为标配。
众所周知,软件的大部分成本并不在最初的开发阶段,而是在于整个生命周期内持续的投入,这包括维护与缺陷修复,健康系统来保持正常运行、故障排查、适配型平台、搭配新场景、技术缺陷的完善以及增加新功能等。
运营团队对于保持软件系统顺利运行至关重要。一个优秀的运营团队通常至少负责以下内容:
良好的可操作性意味着日常工作变得简单,数据系统设计可以在这方面贡献很多,包括:
简化系统设计并不意味着减少系统功能,而主要意味着消除意外方面的复杂性。
消除意外复杂性最好的手段之一是抽象。一个好的抽象设计,可以对外提供干净、易懂的接口;可以再不同应用程序中复用,复用远比多次实现更有效率;另一方面,也能带来更高质量的软件。
一成不变的系统需求几乎没有,想法和目标经常在不断变动。
我们的目标是可以轻松的修改数据系统,使其适应不断变化的需求,这和简单性与抽象性密切相关:简单易懂的系统往往比复杂的系统更容易修改。
数据模型可能是开发软件最重要的部分,他们不仅对软件的编写方式,而且还对如何思考待解决的问题都有深远的影响。
现在最著名的数据模型可能是SQL,它基于关系模型:数据被组织成关系,在SQL中成为表,其中每个关系都是元组的无序集合(在SQL中称为行)。
随着计算机变得越来越强大和网络化,服务目的的日益多样化,关系数据库已经从商业数据处理,顺利推广到了各种各样的用例。
进入21世纪,NoSQL成为推翻关系模式主导地位的又一个竞争者。现在很多新兴的数据库系统总是是打上NoSQL的标签,其含义通常被解释为“不仅仅是SQL”。
采用NoSQL数据库有这样几个驱动因素,包括:
不同的应用程序有不同的需求,在可预见的未来,关系数据库仍将与各种非关系数据存储一起使用,这种思路又是也被称为混合持久化。
如果数据存储在关系表中,那么应用层代码中的对象与表、行和列的数据库模型之间需要一个笨拙的转换成。这种模型之间的脱离有时被称为阻抗失配。
对象-关系映射模型(ORM)框架减少了此转换成所需的样板代码量,但是它们并不能完全隐藏两个模型之间的差异。
无论是存储ID还是存储文本字符串