注:书本链接:GitHub - Vonng/ddia: 《Designing Data-Intensive Application》DDIA中文翻译
注:译者文本网站:简介 · ddia-cn
注:如有侵权,请联系删除
第一章
1、数据挑战量化指标
- 数据量
- 数据复杂程度
- 数据变化速度
2、可靠性
- 应用程序表现出用户所期望的功能。
- 允许用户犯错,允许用户以出乎意料的方式使用软件。
- 在预期的负载和数据量下,性能满足要求。
- 系统能防止未经授权的访问和滥用。
故障(fault):通常定义为系统的一部分状态偏离其标准
失效(failure):系统作为一个整体停止向用户提供服务
容错(fault-tolerant)| 回弹性(resilient):能预料并应对故障的系统特性
3、故障种类:
- 硬件故障
- 软件错误
- 人为错误
4、可伸缩性
-
描述负载
负载参数
扇出(fan-out):从电子工程学中借用的术语,它描述了输入连接到另一个门输出的逻辑门数量。输出需要提供足够的电流来驱动所有连接的输入。在事务处理系统中,我们使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量
注:作者提出推特的案例,在本人看来,可以类比openapi与消息队列,方案一类似openapi,在读需求上需要做更多的操作,方案二类似消息队列,写到某处交由需求方快捷读取。
-
描述性能
- 吞吐量(throughput):即每秒可以处理的记录数量,或者在特定规模数据集上运行作业的总时间
- 响应时间(response time):即客户端发送请求到接收响应之间的时间
延迟和响应时间
延迟(latency) 和 响应时间(response time) 经常用作同义词,但实际上它们并不一样。响应时间是客户所看到的,除了实际处理请求的时间( 服务时间(service time) )之外,还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长,在此期间它处于 休眠(latent) 状态,并等待服务
文中图例体现了响应时间的常规标准
**百分位点(percentiles)**
如果想知道典型场景下用户需要等待多长时间,那么中位数是一个好的度量标准:一半用户请求的响应时间少于响应时间的中位数,另一半服务时间比中位数长。中位数也被称为第 50 百分位点,有时缩写为 p50。注意中位数是关于单个请求的;如果用户同时发出几个请求(在一个会话过程中,或者由于一个页面中包含了多个资源),则至少一个请求比中位数慢的概率远大于 50%。
响应时间的高百分位点(也称为 尾部延迟,即 tail latencies)非常重要,因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准,即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户,也可以说是最有价值的客户 —— 因为他们掏钱了【19】。保证网站响应迅速对于保持客户的满意度非常重要,亚马逊观察到:响应时间增加 100 毫秒,销售量就减少 1%【20】;而另一些报告说:慢 1 秒钟会让客户满意度指标减少 16%【21,22】。
另一方面,优化第 99.99 百分位点(一万个请求中最慢的一个)被认为太昂贵了,不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难,因为它很容易受到随机事件的影响,这超出了控制范围,而且效益也很小。
注:在日常开发生产过程中,往往容易碰到这个问题,是否优化,如何优化,是取舍点。
服务级别目标(SLO, service level objectives) 和 服务级别协议(SLA, service level agreements) 性能交付相关指标制定。
排队延迟(queueing delay):排队延迟(queueing delay) 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务(如受其 CPU 核数的限制),所以只要有少量缓慢的请求就能阻碍后续请求的处理,这种效应有时被称为 头部阻塞(head-of-line blocking) 。即使后续请求在服务器上处理的非常迅速,由于需要等待先前请求完成,客户端最终看到的是缓慢的总体响应时间。
测试方案注意点:为测试系统的可伸缩性而人为产生负载时,产生负载的客户端要独立于响应时间不断发送请求。如果客户端在发送下一个请求之前等待先前的请求完成,这种行为会产生人为排队的效果,使得测试时的队列比现实情况更短,使测量结果产生偏差
在多重调用的后端服务里,高百分位数变得特别重要。即使并行调用,最终用户请求仍然需要等待最慢的并行调用完成。只需要一个缓慢的调用就可以使整个最终用户请求变慢。即使只有一小部分后端调用速度较慢,如果最终用户请求需要多个后端调用,则获得较慢调用的机会也会增加,因此较高比例的最终用户请求速度会变慢(该效果称为尾部延迟放大,即 tail latency amplification)。
个人经验:在微服务框架下,长链路服务间调用容易出现该问题。
- 应对负载方法
适应某个级别负载的架构不太可能应付 10 倍于此的负载。如果你正在开发一个快速增长的服务,那么每次负载发生数量级的增长时,你可能都需要重新考虑架构 —— 或者更频繁。
人们经常讨论 纵向伸缩(scaling up,也称为垂直伸缩,即 vertical scaling,转向更强大的机器)和 横向伸缩(scaling out,也称为水平伸缩,即 horizontal scaling,将负载分布到多台小机器上)之间的对立。跨多台机器分配负载也称为 “无共享(shared-nothing)” 架构。可以在单台机器上运行的系统通常更简单,但高端机器可能非常贵,所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合,因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。
有些系统是 弹性(elastic) 的,这意味着可以在检测到负载增加时自动增加计算资源,而其他系统则是手动伸缩(人工分析容量并决定向系统添加更多的机器)。如果负载 极难预测(highly unpredictable),则弹性系统可能很有用,但手动伸缩系统更简单,并且意外操作可能会更少。
一个良好适配应用的可伸缩架构,是围绕着 假设(assumption) 建立的:哪些操作是常见的?哪些操作是罕见的?这就是所谓负载参数。如果假设最终是错误的,那么为伸缩所做的工程投入就白费了,最糟糕的是适得其反。
5、可维护性
-
软件系统的三个设计原则:
- 可操作性(Operability)
便于运维团队保持系统平稳运行。
- 简单性(Simplicity)
从系统中消除尽可能多的 复杂度(complexity),使新工程师也能轻松理解系统(注意这和用户接口的简单性不一样)。
- 可演化性(evolvability)
使工程师在未来能轻松地对系统进行更改,当需求变化时为新应用场景做适配。也称为 可扩展性(extensibility)、可修改性(modifiability) 或 可塑性(plasticity)。
可操作性:人生苦短,关爱运维
-
简单性:管理复杂度
用于消除 额外复杂度 的最好工具之一是 抽象(abstraction)
可演化性:拥抱变化
本章小结
本章探讨了一些关于数据密集型应用的基本思考方式。这些原则将指导我们阅读本书的其余部分,那里将会深入技术细节。
一个应用必须满足各种需求才称得上有用。有一些 功能需求(functional requirements,即它应该做什么,比如允许以各种方式存储,检索,搜索和处理数据)以及一些 非功能性需求(nonfunctional,即通用属性,例如安全性、可靠性、合规性、可伸缩性、兼容性和可维护性)。在本章详细讨论了可靠性,可伸缩性和可维护性。
可靠性(Reliability) 意味着即使发生故障,系统也能正常工作。故障可能发生在硬件(通常是随机的和不相关的)、软件(通常是系统性的 Bug,很难处理)和人类(不可避免地时不时出错)。容错技术 可以对终端用户隐藏某些类型的故障。
可伸缩性(Scalability) 意味着即使在负载增加的情况下也有保持性能的策略。为了讨论可伸缩性,我们首先需要定量描述负载和性能的方法。我们简要了解了推特主页时间线的例子,介绍描述负载的方法,并将响应时间百分位点作为衡量性能的一种方式。在可伸缩的系统中可以添加 处理容量(processing capacity) 以在高负载下保持可靠。
可维护性(Maintainability) 有许多方面,但实质上是关于工程师和运维团队的生活质量的。良好的抽象可以帮助降低复杂度,并使系统易于修改和适应新的应用场景。良好的可操作性意味着对系统的健康状态具有良好的可见性,并拥有有效的管理手段。
不幸的是,使应用可靠、可伸缩或可维护并不容易。但是某些模式和技术会不断重新出现在不同的应用中。在接下来的几章中,我们将看到一些数据系统的例子,并分析它们如何实现这些目标。