DDIA-Reading2-可靠性,可伸缩性,可维护性

第一章:可靠性、可伸缩性、可维护性

1. 数据密集型应用

  1. 数据密集型应用通常由标准组件构建而成,标准组件提供了很多通用的功能:例如
    1. 存储数据:数据库,databases
    2. 缓存高开销操作,加快读取速度:缓存,caches
    3. 允许用户按关键字搜索数据,或以各种方式对数据进行过滤:搜索索引,search indexes
    4. 向其他进程发送消息,进行异步处理:流处理,stream processing
    5. 定期处理累计的大批量数据:批处理,batch processing

2. 数据系统 Data System

  1. 数据系统包括数据库、消息队列、缓存等工具
  2. 近年出现了许多新的数据存储工具与数据处理工具,它们针对不同应用场景进行优化,不再适合生硬地归入传统类别,比如数据存储可以当做消息队列使用(Redis),消息队列包含类似数据库的持久化保证(Apache Kafka)
  3. 应用程序编程接口(API, Application Programming Interface):将细节向客户端进行隐藏。

3. 可靠性 Maintainablity

  1. 系统在困境(adversity,比如硬件故障、软件故障、人为错误)中仍可正常工作(正确完成功能,并能达到期望的性能水准)。
  2. 故障(Fault)与失效(Failure)
    1. 故障(Fault):造成错误的原因,系统的一部分状态偏离其标准。
    2. 容错(Fault-Tolerant)或韧性(resilient):能够预料并针对故障的系统特性,注意讨论容错时,只有讨论特定类型的错误才有意义。
    3. 失效(Failure):系统作为一个整体停止向用户提供服务。
  3. 反直觉的是,这类容错系统,通过故意触发来提高故障率是有意义的,从而提高故障自然发生时系统能正确处理的信息。
  4. 可靠性十足重要:生产力、收入、名誉、用户感受

3.1. 硬件故障 Hardware Faults

  1. 常见的硬件故障:磁盘崩溃、内存出错、机房断电、有人拔错网线,一般是随机独立的,除非有弱相关性(服务器机架温度)
    1. 磁盘崩溃:磁盘建立RAID
    2. 机房断电:双路电源、热插拔CPU、后备电源等等
  2. 随着数据量和应用计算需求的增加,在亚马逊AWS的云服务平台上,虚拟机实例不可用却没有警告也是常见的,因为云平台首要考虑的是灵活性(Flexibility)和弹性(Elasticity),而不是单机可靠性。
  3. 引入软件容错机制:允许机器失效的系统可以一次修复一个节点,而无需整个停机。

3.2. 软件错误

  1. 内部的系统性错误(Systematic Error):这类错误难以预料,是跨节点相关的,容易导致更多的系统失效
    1. 接受特定的错误输入,导致所有应用服务器示例崩溃的Bug:闰秒与Linux内核错误导致许多应用同时挂掉。
    2. 失控进程会用尽一些共享资源,包括CPU时间、内存、磁盘空间或网络带宽
    3. 系统依赖的服务变慢,没有相应,或者开始返回错误的提示
    4. 级联故障,一个组件的小故障导致另一个组件中的故障,进而触发更多的故障。
  2. 解决方法
    1. 仔细考虑系统中的假设和交互
    2. 彻底的测试
    3. 进程隔离
    4. 允许进程崩溃并重启
    5. 测量、监控并分析生产环境中的系统行为
    6. 系统自检,发现差异(Discrepancy)后预警

3.3. 人为错误

  1. 运维配置错误是导致服务中断的首要原因
  2. 如何尽量避免人为错误?
    1. 以最小化犯错机会的方式设计系统:平衡接口限制 与 使用简易程度
    2. 将人们最容易犯错的地方与可能导致失效的地方解耦(decouple):提供沙盒来使用真实数据安全地探索和实验。
    3. 在各个层次进行彻底的测试,尽可能覆盖边缘场景。
    4. 允许从人为错误中简单快速地恢复,以最大限度的减少失效情况带来的情况:快速回滚配置变更、分批发布新代码、提供数据重算工具
    5. 配置详细且明确的监控,比如性能指标和错误率。
    6. 良好的管理实践与充分的培训

4. 可伸缩性 Scalability

  1. 有合理的办法应对系统的增长(数据量、流量、复杂性)
  2. 服务降级(degradation)的常见原因:负载增加

4.1. 描述负载

  1. 负载可以用负载参数(load parameters)的数字来描述,参数的最佳选择取决于系统架构,比如读写率、缓存命中率等等。
  2. 实例:推特的扇出(每个用户关注了很多人,也被很多人关注),用户发送推特后,如何维护主页时间线
    1. 方案一:将新推文加入全局推文集合,之后其他用户查看时进行联表查询。
    2. 方案二:每个用户的主页时间线维护缓存,关注用户发送推文后,将推文加入到主页时间线中,问题:部分用户拥有千万级粉丝,这会非常消耗性能。
    3. 最终方案:方案一 + 方案二,大部分用户的推文会被扇出到分析主页时间线缓存,但是少数拥有海量粉丝的用户则采用方案一,最后用户的主页时间线将是把主页时间线缓存和查询混合。

4.2. 描述性能

  1. 关注点
    1. 增加负载参数并保持系统资源(CPU、内存、网络带宽等)不变时,系统性能将受到什么影响?
    2. 增加负载参数并希望保持性能不变时,需要增加多少系统资源?
  2. 常见的性能指标
    1. 吞吐量 Throughput:常见于Hadoop这样的批处理系统,即每秒可以处理的记录数量,或者特定规模数据集上作业的总时间。
    2. 响应时间 Response Time
      1. 客户端发送请求到接收响应之间的时间。
      2. 包括实际处理请求的时间(服务时间(service time))之外,还包括网络延迟和排队延迟。
        1. 排队延迟(Queueing delay)通常占了高百分位点处响应时间的很大部分,往往出现头部阻塞效应。
        2. 头部阻塞(head-of-line blocking):少量缓慢的请求就能阻碍后续请求的处理。
      3. 往往通过可以测量的数值分布(distribution)来衡量响应时间。
    3. 延迟:是某些请求等待处理的持续时长,在此期间处于休眠(latent)状态,并等待服务。
  3. 不同标准进行衡量
    1. 算术平均值:服务的平均响应时间
    2. 百分位点(常用)
      1. 中位数(p50)是一个好的度量标准
      2. 响应时间的高百分位点(也称为尾部延迟,即tail latencies)非常重要。对于亚马逊而言,优化P999相对收益较大,而优化P9999被认为比较昂贵。
      3. 常用于服务级别目标(SLO, service level objectives)服务级别协议(SLA, service level agreements),即定义服务预期性能和可用性的合同。比如响应时间的中位数小于200ms,且99.9百分位点低于1s,则认为服务工作正常。
  4. 实践中的百分位点
    1. 在多重调用的后端服务中,高百分位数变得特别重要。
    2. 如果你想将响应时间百分点添加到服务的监控仪表板,则需要持续有效的计算他们(计算最近的10分钟):降低CPU和内存成本(前向衰减、T-Digest或hdrHistogram)来计算百分位数的近似值。

4.3. 应对负载的方法

  1. 常见的方法
    1. 纵向负载(scaling up,也称为垂直伸缩,即vertical scaling):转向更强大的机器
    2. 横向伸缩(scaling out,也称为水平伸缩,即horizontal scaling):将负载分布到多台小机器上
  2. 无共享(Shard-nothing)架构:跨多台机器分配负载
  3. 弹性(Elastic)系统:意味着可以在检测到负载增加时自动增加计算资源。
    1. 跨多台机器部署无状态服务(stateless service)非常简单。
    2. 将带状态的数据系统从单节点变为分布式配置则可能引入许多额外复杂度。
    3. 直到伸缩成本或可用性需求迫使其改为分布式
  4. 一个良好适配应用的可伸缩架构,是围绕着假设(assumption)建立的:哪些操作是常见的?哪些操作是罕见的?

5. 可维护性 Maintainablity

  1. 许多不同的人(工程师、运维)在不同的生命周期,都能高效地在系统上工作(是的系统保持吸纳有行为,并适应新的应用场景)
  2. 关注软件系统的三个设计原则
    1. 可操作性(Operability):便于运维团队保持系统平稳运行
    2. 简单性(Simplicity):从系统中消除尽可能多的复杂度(Complexity),使得新工程师也能轻松理解系统(和用户接口的简单性不一样)
    3. 可演化性(Evolvability):使工程师在未来能轻松地应对系统进行更改,当需求变化时为新应用场景做适配,也称为可伸缩性(extensibility)、可修改性(modifiability)或可塑性(Plasticity)

5.1. 可操作性

  1. 良好的可操作性意味着更轻松的日常工作,进而运维团队能专注于高价值的事情。

5.2. 简单性:管理复杂度

  1. 额外的复杂度:由具体实现中涌现,而非(从用户角度看,系统所解决的)问题本身固有的复杂度。
  2. 消除复杂度的最好工具之一:抽象(Abstraction)

5.3. 可演化性:拥抱变化

  1. 在组织流程方面,敏捷(agile)工作模式为适应变化提供一个框架。
  2. 在频繁变化的环境中开发软件很有帮助的技术工具和模式,如测试驱动开发(TDD,test-driven development)和重构(refactoring)
  3. 可演化性指代了数据系统层面的敏捷性

DDIA-Reading2-可靠性,可伸缩性,可维护性
https://spricoder.github.io/2022/02/18/Designing-Data-Intensive-Applications-Reading/Designing-Data-Intensive-Applications-Reading2-%E5%8F%AF%E9%9D%A0%E6%80%A7-%E5%8F%AF%E4%BC%B8%E7%BC%A9%E6%80%A7-%E5%8F%AF%E7%BB%B4%E6%8A%A4%E6%80%A7/
作者
SpriCoder
发布于
2022年2月18日
许可协议