开篇
目前大部分程序都是IO密集型应用,这些应用程序通常不受 CPU性能的限制,而是更多地受到数据量、复杂性以及数据变更速度的影响。而CPU密集型应用瓶颈主要在CPU比如图像处理、机器学习等。本书关注重点在IO密集型应用。
密集型应用一般由标准组件构成、组建一般提供如下功能
● 数据库 用于数据存储、确保数据可以被后续的操作或其他程序找到
● 缓存 用于存储经常访问的数据以及计算成本较高的结果
● 搜索索引 允许用户按照关键字搜索数据、或以各种方式对数据进行过滤
● 流处理 用于向其他进程发送消息、用于异步处理
● 批处理 定期处理大量累积数据
对于同一个组建现实中根据不同的需求有不同的设计、 在我们实现我们自己的应用时、能够根据自己的需求选择正确的应用组件、本书就是对这些组件的实现原理与应用的探索。
数据系统
一个典型的数据系统架构可能如图所示
● 数据库(Primary database)负责数据持久化
● 缓存(In-memory cache)负责优化查询速度
● 全文索引(Full-text index)负责优化查询方式
● 消息队列(Message queue)负责异步任务
此时、你不仅仅是应用程序开发人员也是数据系统设计人员了(组建都归为数据系统类别)
主要内容
主要论述在软件系统中比较重要的三个概念
- Reliable(可用性) 系统在困境中依然能够正常工作
- Scalable(可扩展性) 有合理的办法能够应对系统的增长(流量、数据量)
- Maintainable(可维护性) 许多不同的人在不同的生命周期能够高效的在系统上工作
Reliable
可靠性
可靠性是指系统即使在出现故障的情况下也能持续正确运行并满足性能标准的特性。它通常包括以下几个方面:
● 系统功能:系统表现出用户所期望的功能。
● 用户错误处理:系统允许用户犯错并以意外的方式使用软件。
● 安全性:系统能防止未经授权的访问和滥用。
● 性能标准:在预期的负载和数据量下,系统性能满足要求。
导致可靠性问题原因
● 硬件故障:如硬盘崩溃、RAM故障等。
● 软件错误:如系统性的BUG,导致服务崩溃或性能下降。
● 人为错误:操作失误,如配置错误或误操作,这些是导致服务中断的主要原因之一。
解决措施
● 硬件冗余:使用RAID、备用电源等增加硬件冗余,减少单点故障的风险。
● 软件容错机制:如允许节点失效而不影响整个系统,通过软件设计来容忍硬件问题。
● 故障注入:通过故意引发故障(例如Netflix的Chaos Monkey),确保容错机制的有效性。
● 系统设计:设计系统以最小化人为错误,并提供彻底的测试、进程隔离、快速故障恢复等。
● 监控和自检:使用详细的监控和遥测技术,以发现和预警潜在的问题。
● 管理和培训:实施良好的管理实践和充分的培训,降低人为错误的风险。
Scalable
可伸缩性
可伸缩性描述了一个系统在面对负载增长时保持性能不变的能力。讨论可伸缩性意味着考虑如何增加计算资源以应对负载的增加,并理解这种增加对系统性能的影响。
描述负载
负载需要通过负载参数(如每秒请求次数、数据库的读写比率等)来定义和量化。需要根据自己的情景进行负载描述。书中提到Twitter发文的负载主要在扇出
描述性能
性能可以通过吞吐量(如每秒可以处理的记录数量)和响应时间(客户端发送请求到接收响应的时间)来衡量。响应时间通常以百分位数表示,例如p50(中位数)、p95、p99等。
注:p90是指90的请求都在定义的阈值之下响应。高百分位数(尾部延迟)对用户体验至关重要,因为它们代表了最慢的请求。响应时间的高百分位数对于定义服务级别目标(SLO)和服务级别协议(SLA)非常重要。
应对系统伸缩
- 系统架构需要能够适应负载的增长,这可能需要重新考虑架构来适应不同级别的负载。
- 纵向伸缩(scaling up)指的是增加单个节点的资源,而横向伸缩(scaling out)指的是增加更多节点来分担负载。横向伸缩会增加系统的复杂性、因此在纵向扩展可以满足需求时尽量用纵向扩展
- 在产品的早期阶段,快速迭代和响应用户需求通常比构建可伸缩到假想未来负载的系统更为重要
- 无状态服务比带状态服务更容易伸缩。带状态的数据系统从单节点到分布式配置的转变可能会带来额外的复杂性。
Maintainable
可维护性在软件系统的整个生命周期中占据着核心地位。以下是可维护性的三个关键组成部分及其意义:
可操作性(Operability)
定义:指的是使运维团队能够有效地管理系统并确保其平稳运行的系统特性。
重要性:运维团队负责监控系统健康,追踪问题,更新系统,预防问题等。良好的可操作性可以减轻运维团队的负担,使他们能够专注于增加价值的活动。
实现:通过提供良好的监控、支持自动化、文档、预测性行为、自我修复机制和避免单点故障等方式来实现。
简单性(Simplicity)
定义:从系统中消除不必要的复杂性,使新工程师能够更轻松地理解系统。
重要性:系统的复杂性是维护成本高昂的主要原因之一。简单性有助于减轻这种负担,并减少因理解系统而产生的错误。
实现:通过创建清晰的抽象层和接口,消除重复代码,模块化和解耦,以及通过代码清晰和一致的命名来管理复杂性。
可演化性(Evolvability)
定义:使工程师能够在未来轻松地修改系统,以适应变化的需求或适配新的应用场景。
重要性:随着时间的推移,几乎所有系统都需要适应变化。一个易于演化的系统可以更快地响应这些变化,减少扩展或修改所需的工作量。
实现:通过保持代码的整洁、使用测试驱动开发、频繁重构以及选择可以灵活适应变化的架构和技术栈来实现。