跳转至

第一章:可靠性、可扩展与可维护的应用⚓︎

数据密集型应用(Data-Intensive Applications)通常由标准化的模块构建而成,包括数据库、缓存、搜索索引、流处理与批处理框架。本章定义了评估和设计此类数据系统的三大核心基础要求:可靠性(Reliability)可扩展性(Scalability)可维护性(Maintainability)

可靠性(Reliability)⚓︎

可靠性是指系统在面临各类硬件、软件配置甚至人为操作的故障(Faults)时,依然能够继续正常且正确运行(维持预期的性能与功能)的能力。系统的最终目标是从容忍故障(Fault-Tolerant)走向弹性(Resilient)。

故障主要划分为以下三类:

  • 硬件故障(Hardware Faults):如硬盘损坏、内存故障或断电。
    • 解决机制:主要依赖硬件冗余(如 RAID 磁盘阵列、双电源配置)以及架构层面的多节点软件冗余设计。当代云环境假设物理机故障是常态,系统通常被设计为能承受单机宕机而不影响整体可用性。
  • 软件错误(Software Errors):指系统内那些触发条件关联且跨节点蔓延的系统性错误。如处理特定输入导致的级联崩溃、由于共享资源耗尽而造成的死锁与阻塞。
    • 解决机制:无法通过简单的硬件冗余解决。需依赖严格的系统假设隔离、彻底的测试、进程计算隔离以及在生产环境中进行运行时的进程监控。
  • 人为错误(Human Errors):配置错误或操作失误是导致系统停机的主要原因。
    • 解决机制:解耦易出错的地方与核心机制之间的耦合度;提供充分的沙箱演练环境;支持快速恢复(如代码和配置的快速回滚);完善全面的遥测与监控(Telemetry)。

可扩展性(Scalability)⚓︎

可扩展性描述了系统应对负载增长的能力。讨论扩展性通常分为“描述负载”、“描述性能”与“应对负载的架构解法”三个维度。

描述负载与 Twitter Fan-out 实例⚓︎

系统需要通过具体的负载参数(Load Parameters)来量化当前的流量特征。参数可以是每秒的 Web 请求数、数据库的读写比例或活跃用户数。

DDIA 借助 Twitter 的主页时间线(Home Timeline)揭示了扩展性的核心矛盾——扇出(Fan-out):

  • Push 模式(写时扇出):用户发推时,系统将该推文异步插入到所有关注者的主页缓存列表中。这使得读取主页极大提速,但在面对拥有海量粉丝的“大V”用户发推时,单次写操作会导致上千万次的异步缓存更新,产生严峻的写负载高峰。
  • Pull 模式(读时扇出):用户在发推时仅存入全局数据库;当其他用户刷新主页时,实时查询其关注的所有人并聚合排序。这降低了写入压力,但大幅增加了读取的延迟开销。
  • 混合模式解法:对普通用户采取 Push 模式预计算;对“大V”用户采取 Pull 模式,在读取时将“大V”的推文与普通用户的预计算列表进行动态合并。

描述性能与尾部延迟(Tail Latencies)⚓︎

当量化了负载后,需要评估负载增加对性能的影响。此时评估系统性能不应只关注平均响应时间(Average/Mean),而必须采用百分位数(Percentiles)

少数异常缓慢的请求被称为尾部延迟(Tail Latencies),它们直接决定了真实用户的体验感受。常用分位数包括:

  • p50:中位数,即 50% 的请求快于该值。
  • p95p99:高位百分比,意味着 95% 或 99% 的请求在该数值内返回。这通常被作为应用服务等级协议(SLA)的标准。
  • p999:99.9% 的请求,通常与处于业务系统最核心且拥有最多关注、最多数据量的高净值用户相关。

高精度延迟统计与聚合结构

在大规模分布式系统中计算高分位延迟极其耗费内存。当前工业界往往采用以下近似聚合数据结构来保证精度与性能的平衡:

  • HdrHistogram:用于高精度记录数值分布,在很宽的动态范围(如 1 微秒到 1 小时)内记录请求延迟并支持对数据进行分位数、平均值、标准差等多维度统计。对极其细微的尾部延迟分析(如系统停顿、GC 开销监控)非常友好。
  • t-digest:适合利用极小内存计算海量日志的大规模在线分位数的数据结构。通过动态聚类区间,实现在多台机器汇总后依然保持 p99 尾部的极高近似计算精确度。
  • 正向衰减(Forward Decay):用于时间序列平滑处理,核心思想为使陈旧数据的影响呈指数级递减。常搭配蓄水池抽样(Reservoir Sampling)应用,确保监控图表总是反映系统“最近一段窗口”的真实负载趋势。

应对负载的策略⚓︎

  • 垂直扩展(Scaling Up):提升单机设备的硬件配置(CPU、内存、磁盘阵列)。
  • 水平扩展(Scaling Out):采用无共享架构(Shared-Nothing Architecture),将负载分摊或切片至多台低端机器节点中。

可维护性(Maintainability)⚓︎

软件生命周期中绝大部分的成本消耗在维护阶段(修复漏洞、保持系统运行、适应新平台、偿还技术债务),系统设计需要满足以下三个设计原则以降低后续运维开销:

  • 可操作性(Operability):使运维团队能够轻松地保持系统平稳运行。提供良好的监控预见性,为生产系统暴露所有内部状态与指标,支持清晰的部署文档与自动化管理脚本。
  • 简单性(Simplicity):致力于在工程层面消除不必要的复杂性(Accidental Complexity)。应对复杂软件的最优工具是抽象(Abstraction)。优秀的抽象层通过隐藏大量实现细节,使系统容易理解、重构并在不同模块复用。
  • 可演化性(Evolvability):使工程师未来能够轻松且低风险地对系统进行二次修改和迭代,从而适应非预期的需求变更(可塑性/敏捷性)。通常系统解耦越彻底,其可演化性越高。

评论