为什么 Edge Cases 在 Distributed Systems 中很重要

发布: (2025年12月29日 GMT+8 21:16)
12 min read
原文: Dev.to

Source: Dev.to

我一直在阅读《Designing Data‑Intensive Applications》,它让我更加注意到我们在日常工程中很少质疑的假设。让我印象深刻的不是这本书的技术深度,而是它逐步改变了我们看待系统的方式。它把那些因为大多数情况下都能正常工作而被忽视的假设提了出来。作为开发者,我们在很大程度上依赖书中所说的 工程俗语——这些想法之所以流传,是因为它们在实践中往往成立,而不一定是因为我们完全理解它们何时会失效。

在阅读过程中,我注意到这本书经常鼓励我们超越表面的行为去观察。许多系统从外部看起来很相似:它们提供相似的 API,使用熟悉的工具,并且在正常条件下表现良好。但这种表面的相似性可能会产生误导。一旦出现与我们预期不符的情况,那些看似相似的系统可能会表现出截然不同的行为。

这些假设大多数在单独考虑时是合理的。挑战在于,当系统规模扩大并开始相互交互时,它们会变得更加脆弱。我们所假设的与实际发生的之间的差距,正是许多问题的根源所在。

闰秒事件

在阅读本书之前,我并未听说过闰秒事件。偶然看到它后,我产生了兴趣,查阅了实际发生了什么以及系统随后是如何适应的。

闰秒事件最常指 2012 年 6 月 30 日 发生的广泛技术中断,当时为使原子钟与地球自转保持同步,向原子钟中添加了一个额外的秒。这导致出现了一个罕见的情况:一分钟包含 61 秒,产生了时间戳 23:59:60

  • 理论上,这一调整是明确定义的。
  • 实际上,许多系统并未设计为能够处理时间出现暂停、重复或倒退的情形。

最严重的问题之一来源于 Linux 内核高分辨率计时器的一个 bug。当闰秒出现时,一些系统进入了 活锁(live‑lock) 状态。CPU 使用率飙升至 100 %,因为进程被困在紧密循环中,导致系统无法前进。

由于大量基础设施共享了相似的时间假设,影响迅速蔓延。诸如 Reddit、LinkedIn、Mozilla、Yelp 等主要网站都经历了部分或完全宕机。例如,Reddit 曾不可用超过一小时。除了网络服务之外,Amadeus 航空预订系统也出现故障,导致澳大利亚数百个航班延误。

基于 Java 的系统——包括 CassandraHadoop 等技术——尤其脆弱,因为它们依赖相同的底层 Linux 计时器。一旦操作系统层面的时间表现异常,上层软件便继承了这一问题。

让我印象深刻的是,不同系统部件的响应方式各不相同:

  • 有些组件尝试向前推进时间。
  • 另一些则暂停或重试,等待时间前进。

这种不一致产生了类似死锁或活锁的情形,但并非因为锁定逻辑错误,而是 时间本身成为了一个不可靠的共享依赖

后续影响与缓解措施

  • 闰秒平滑(Leap smearing) – Google 与 Meta 采用一种方法,将额外的秒在数小时内逐渐摊开。从系统角度看,时间既不会跳跃也不会停滞;只是在短时间窗口内稍微变慢。
  • Linux 改进 – 内核的时间管理得到增强,能够更好地处理闰秒而不会导致线程意外自旋或停滞。
  • 政策变更 – 国际时间管理机构在 2022 年投票决定在 2035 年之前彻底废除闰秒。目前尚未安排新的闰秒。

让我记忆犹新的点是,问题并不只是额外的那一秒,而是 假设时间在所有系统中始终一致且普遍认同 的前提被打破。

故障、失效与规模化现实

One of the most important distinctions the book makes is between a fault and a failure:

概念定义
Fault系统的一部分偏离了预期行为。
Failure整个系统停止提供用户期望的服务。

That difference matters more than it sounds.

  • 故障是正常的。磁盘会失效。网络会变慢。时钟会漂移。人会犯错。
  • 在大规模系统中,这些并非例外——它们是预期的。

可靠的系统并不是从不出错的系统;而是即使系统的某些部分出现异常,也能继续运行的系统。

具体示例

Hard disks are reported to have a mean time to failure (MTTF) of about 10–50 years. That sounds reassuring until you operate thousands of disks. At that scale, disk failures are not rare events; they happen regularly. What feels unusual on a single machine becomes normal behavior in a large system.

The same idea shows up in fan‑out patterns, where a single event triggers many downstream actions. These patterns are powerful, but they also increase the impact of small issues. A delay or error in one place can quickly spread across multiple services.

When you add impedance mismatch between systems that were never designed to fit together naturally, complexity grows even faster. Over time, systems accumulate complexity that makes them harder to understand, operate, and evolve safely.

性能不仅仅是速度

另一个让我印象深刻的细微教训是 latencyresponse time 之间的区别。这两个术语常被交替使用,但它们并不相同。

  • Response time – 用户的实际感受。它包括处理时间、网络延迟以及在队列中等待的时间。
  • Latency – 请求在 被处理之前 所花费的等待时间。

从用户的角度来看,这些不同的指标很重要,因为它们会影响感知性能和整体系统可靠性。

Source:

为人而设计的系统

本书还挑战了一些关于性能的常见假设。出人意料的是,内存数据库 并不 总是因为避免磁盘读取而更快。它们之所以更快,往往是因为避免了将内存数据结构编码为适合磁盘存储的格式所带来的开销。这个细节会改变你对优化的思考方式,以及性能瓶颈真正来源的判断。

可靠性、可扩展性和可维护性

随着章节的推进,书中不断回到可靠性、可扩展性和可维护性。我欣赏的是,这些 并不是 纯粹的技术目标——它们是人的目标。

  • 可操作 – 团队能够让系统持续运行。
  • 可理解 – 新工程师能够快速掌握设计。
  • 可演进 – 系统能够随需求变化而适应。

积累了不必要复杂性的系统不仅会拖慢机器的运行速度;它们还会 拖慢人。它们使学习变得更困难,增加风险,并让错误的代价更高。这里的简洁 并不 意味着功能更少;而是指隐藏假设更少,非必要的复杂性更低。

关键要点

考虑边缘情况并不是编程中的新概念。大多数开发者很早就学会处理空值、无效输入以及明显的边界条件。这部分是理所当然的。

更不明显的——尤其是系统规模扩大时——是一类不同的边缘情况。这些 不是 逻辑错误,而是经常被视为理所当然的假设。

  • 时间 被假设为平稳向前推进,尽管它可能漂移、停滞或重复。
  • 磁盘 常被假设很少故障,但实际上故障在统计上是预期的。
  • 网络 被视为要么在线要么离线,而实际上故障往往是部分的,表现为延迟、丢包或整体性能下降。
  • 消息 被假设只会到达一次且顺序正确,然而重复和乱序是常见的。

在小规模时,这些假设通常足够好。规模扩大后,它们开始相互作用。

时钟稍有漂移、磁盘偶尔故障、或消息迟到单独来看并不算问题。但当这些微小偏差在大量机器和服务中同时出现时,它们就不再是边缘情况,而成为 正常行为——即故障转变为失效的临界点。

该怎么做

  • 不要尝试处理每一种可能的情形——这不现实。
  • 有意识地 确定系统依赖了哪些假设。
  • 规划好当这些假设失效时的应对措施

为 happy path 设计是必要的。考虑偏差进行设计才是让系统随时间保持稳定的关键。


如果你有共鸣,欢迎分享在真实系统中哪个假设的失效让你最感意外。

Back to Blog

相关文章

阅读更多 »

Knotlog:面向 PHP 的广泛日志记录

为什么日志记录糟糕以及如何修复它——正如 loggingsucks.com(https://loggingsucks.com/)精彩阐述的,传统的日志记录在现代环境中根本存在缺陷……

第7集:'Join'税 vs. 'Storage'税

SQL 与 NoSQL 的权衡 当我们在系统设计中讨论 SQL 与 NoSQL 时,已经超越了语法层面,关注核心的权衡。 在真实的系统中,你需要选择数据……

系统设计快速指南

系统设计是规模的语言,每位工程师都需要掌握它。我创建了这份 1 页的 Quick Guide,帮助你解码复杂的系统设计主题……