cisca
发布于 2026-04-20 / 9 阅读
0
0

《DDIA》第一章:重新认识可靠、可扩展与可维护的系统

第一章的核心其实是为整本书搭建一个评估数据系统的宏观框架:可靠性、可扩展性与可维护性。在边读边思考的过程中,我产生了许多“顿悟”时刻,在这里以博客的形式记录下来,希望能给同样在啃这本书的朋友一些启发。

1. 可靠性(Reliability):不仅仅是“没崩”,甚至要主动“搞破坏”

在过去,我有时会把系统的“可靠性”和 CAP 定理中的“可用性(Availability)”混为一谈。但在阅读后我意识到,它们有着本质的区别。可用性更像是一个微观指标,关心的是系统能不能连上、有没有非错误的响应(哪怕返回的是旧数据);而可靠性则是宏观的,不仅要求系统在面对硬件故障、软件 Bug 甚至人类操作失误时能继续工作,还要求它“算得对“。比如一个能打开但购物车金额算错的系统,是可用但不可靠的 。

为了保障可靠性,书里提到了一个让我大开眼界的概念——Netflix 的“混乱猴子”(Chaos Monkey)系统。传统的运维都是小心翼翼地保护服务器生怕它们宕机,而 Chaos Monkey 却是在每天的工作时间段内,随机、强行地“杀掉”几台生产环境的服务器!这种反其道而行之的“自残”哲学极为精妙:既然分布式系统里硬件故障不可避免,不如主动引爆,逼迫工程师在写代码的第一天就必须做好无状态化、自动重试等容错机制。

2. 可扩展性(Scalability):从推特的架构演进,看清背后的权衡

讨论可扩展性时,系统如何应对不断增长的负载(Load)是核心。书里举了推特(Twitter)主页时间线的例子,这个案例让我联想到了很多关于消息队列和架构演进的知识。

一开始,推特采用的是读时联结的关系型查询(拉取模型),但这导致读取压力极大。后来采用了 Fanout(扇出)的做法,这本质上是一个基于“推模式”的发布-订阅架构:当用户发推时,后台会像 RabbitMQ 的 fanout 交换机一样,直接把推文复制并插入到每个粉丝的时间线缓存中。这是一个教科书级别的“空间换时间”策略:写入时多做工作,换取用户打开 App 时极快的读取性能。

但我顺着这个思路算了一笔账:如果拥有 1.3 亿粉丝的马斯克发一条 100B 的推文,瞬间就会产生高达 13GB 的写入分发量,这会导致严重的网络拥堵(惊群效应),排在后面的粉丝可能半天都刷不出推文。现实中推特的解法非常巧妙:混合架构。普通用户发推走 Fanout 缓存推送,而大 V 发推则只存入全局高速缓存;普通粉丝打开推特时,系统会将自己的缓存时间线与主动拉取的大 V 推文在内存中进行合并排序。这种妥协与结合,正是系统可扩展性设计的魅力所在。

“平均数”与长尾效应

在应对负载的性能指标上,DDIA 颠覆了我的常识。以前看监控,我总觉得看中位数(p50)就足够了。但现实是,p50 只意味着有一半的请求比它快,我们可能忽略了另外一半极其糟糕的用户体验。

工业上的系统都在死盯 p99 甚至 p99.9(最慢的 1% 或 0.1%)的长尾延迟。一方面,遭遇最慢响应的客户往往是购物车数据庞大的高价值老客户;另一方面,分布式系统存在可怕的“长尾放大效应”。假设一个操作要调用 100 个微服务,即使每个服务只有 1% 的概率卡顿,最终也会有超过 60% 的用户请求被这 1% 拖慢。

既然物理上的网络抖动和 GC 导致的长尾延迟难以彻底根除,前端交互就成了最后的防线。比如利用“乐观 UI”假装处理成功瞬间亮起红心,或者用骨架屏转移注意力,这都是用感知性能优化来掩盖系统底层不可避免的长尾效应。

3. 可维护性(Maintainability):一个容易踩坑的翻译盲区

最后是可维护性。在讨论时我发现了一个中文技术圈极其容易踩坑的“翻译重灾区“。

如果现在产品经理要求在原有的系统中“添加一个新功能”,这属于可扩展性还是可维护性?我一开始有些犹豫,但其实这绝对属于可维护性

中文经常把两者混为一谈,但在 DDIA 中:

  • Scalability(可扩展性):专指应对机器、数据和并发流量增加时的架构策略。
  • Maintainability(可维护性):包含可演化性(Evolvability),指的是系统在面对不断变化的业务需求和新功能时,修改代码的难易程度。

理清了这个界限,就不会在讨论架构时鸡同鸭讲了。


评论