IT 论文精读 · PAPER 56
Fox & Brewer · UC Berkeley · HotOS-VII 1999
1999 年,两位伯克利研究者 Fox 与 Brewer(Brewer 一年后提出了大名鼎鼎的 CAP 定理)问了一个到今天还管用的问题:一个由成千上万台机器撑起来的大网站,坏了几台,我们习惯以为它要么「能用」、要么「挂了」——非黑即白。可真实的大服务从来不是这样。这篇论文主张:别把「可用」当成一个开关,把它当成一根能调的旋钮。
你在大促时抢东西,页面很慢,或者搜索只返回了一部分结果、某个小功能暂时点不动——但整个网站没崩,你还是把东西买到了。这不是运气,是被设计出来的「优雅降级」。这篇论文,就是最早把「怎么优雅地坏一点」讲成一套方法的文章之一。
过去大家只有两个非黑即白的词:系统「一致」或不一致、「可用」或不可用。这篇把「可用」拆成两个能打分的量:
一个搜索少返回了几条结果,就是「产出率满分、收获度打了个折」。这么一拆,「坏一点」立刻多出了两种便宜的坏法可以选。
关键洞察:一台机器每秒能吞的「数据量 × 请求数」是有物理上限的。坏了几台,这个总量就少一块。少的这一块你可以选择怎么承担——要么每个答案少用点数据(降收获度),要么少答几个请求、把接下的答全(降产出率)。同一场故障,两种截然不同的体验,全由你提前设计决定。还有一招:把系统拆成互不牵连的小块,把「必须绝对准确」的那一小撮数据圈在最小的范围里,其余部分尽量做成「可以先凑合、之后补齐」——故障就只砸中一小块。
一年后,Brewer 把这里的取舍正式提炼成 CAP:一致、可用、分区容错,三者不可兼得。今天几乎所有大规模服务的「降级」「限流保核心」「最终一致」「成功率 SLO」,思想都能追到这篇。诚实说一句代价:这套「答一半也行」只对搜索、推荐这类软性场景管用;查银行余额时,一个不完整或过时的答案就是错的,宁可不答。
别把「可用」当开关;把它拆成「答上了几个」(产出率)和「答得多完整」(收获度)两把旋钮——故障来时,选择怎样优雅地坏一点,而不是整个倒下。
想看 DQ 原则、复制 vs 划分的机制图与 CAP 的来龙去脉? → 切到精读版
1999 年 Fox 与 Brewer 指出:大规模服务的「可用性」不该是非黑即白的开关,而应拆成两个可度量、可交易的量——产出率(yield,成功完成的请求占比)与收获度(harvest,单个回答里覆盖到的数据占比)。据此把「故障时怎么办」从「要么撑住、要么倒下」变成一道显式的选择题:同一场故障,可以选择降低收获度(答案不完整但照样答)或降低产出率(答少一点但答全),从而优雅降级、在故障与分区中仍尽量可用。这篇短文也第一次成文地摆出「强一致与高可用不可兼得」的取舍——一年后被 Brewer 提炼为 CAP 定理。
作者 Armando Fox 与 Eric Brewer,均来自加州大学伯克利分校;文章是 1999 年 HotOS-VII 研讨会上的一篇立场短文(position paper),篇幅很小、志在提出框架而非做实验。Brewer 此前联合创办搜索引擎公司 Inktomi、亲历大规模集群服务的工程,本文的例子多以搜索集群为背景。它上承作者们更早的集群服务工作(1997 年提出 BASE 思路的 Cluster-Based Scalable Network Services),下启 2000 年 Brewer 在 PODC 的 CAP 猜想与 2002 年 Gilbert–Lynch 的形式化证明——本文正是 CAP 之前那块奠基石。
90 年代末,互联网服务规模暴涨,机器成百上千地堆,故障从「异常」变成「常态」——总有那么几台在坏。当时衡量可靠性的老办法有两处不趁手:
更根本的一堵墙:作者点明,强一致(所有副本随时一致)与高可用,在网络可能分区时无法兼得。网络一旦把集群切成互不通信的两半,你要么让两边都继续服务(放弃一致,各写各的),要么为保一致让一边停下(放弃可用)。这不是工程没做到位,而是硬性取舍。既然一致与可用不能全要、故障又是常态,作者主张:与其追求「永不失败」,不如设计「失败时怎样体面地降级」。
产出率 yield = 成功完成的请求数 ÷ 收到的请求数。它和 uptime 的差别在于按请求加权:高峰期垮掉,分母里请求多,yield 掉得狠,正好反映「高峰故障更贵」。直觉:yield 回答「你到底答上了几个」。
收获度 harvest = 回答里反映到的数据 ÷ 完整数据。它承认「回答可以是部分的」:一个搜索少扫了 5% 的库、仍返回 95% 数据里的结果,harvest = 95%。直觉:harvest 回答「你答得有多完整」。
把这两者拆开,最大的收益是:故障不再只有「答」与「不答」两种结局,中间多出一整条「答得不那么全」的缓冲带,可以拿来换取「还能答」。
作者提出一个经验规律:在系统接近满载时,每个请求用到的数据量 D 与每秒请求数 Q 的乘积近似是个常数——它由系统的物理容量(内存带宽、磁盘、网络)封顶。写作 D × Q ≈ 常数,白话就是「数据管够和请求管够是同一块蛋糕,此消彼长」。
这条规律把故障翻译成一句大白话:一场故障拿走了系统 x% 的容量,就等于把 DQ 砍掉 x%。这一刀你想砍在 D 上还是 Q 上,自己选。
关键在于:DQ 逼你承认「降级不可避免」,真正的设计自由只剩「往哪降」——而这恰恰能对准业务(要完整,还是要每个都有回音)。
同一场「坏掉一台节点」的故障,数据怎么摆,决定了你丢产出率还是丢收获度:
作者还给了个反直觉的观察:在接近满载时,复制并不能凭空变出更多容量——因为 DQ 被物理上限封死,复制和划分的总 DQ 一样。复制真正买到的不是「更大容量」,而是「故障时保住收获度」。所以复制 vs 划分不是「谁更快」,而是「你更想保住哪个量」。
最后一招在架构层面:把应用拆成互相正交、独立失败的子系统,各自用自己的 harvest / yield 策略;并把「必须强一致、必须持久」的硬状态圈进尽量小的一块,其余尽量做成软状态(可重建、容忍旧值、最终一致)。这样一处故障只砸中一小块,大部分系统仍能高可用地服务。作者强调机制要正交、可组合——状态与故障都被限制在局部,不会牵一发而动全身。
这是一篇 HotOS 立场短文,没有跑分表格,它的「成果」是一套沿用至今的思考框架和真实系统的印证,而非实验数字。要点:
(本文以论证与设计原则为主,此处不杜撰具体百分比或基准数字。)
① 一句话:把「可用性」拆成产出率(答上几个)与收获度(答得多全)两个可度量、可交易的量,让故障从「撑住 / 倒下」变成「怎么优雅地降级」。
② 痛点:大规模下故障是常态;二值可用抓不住「大部分好、少数坏」的中间态,uptime 又低估高峰故障;且强一致与高可用在分区下不可兼得。
③ yield = 完成请求 / 收到请求(按请求加权,故障在高峰更贵);harvest = 回答覆盖数据 / 完整数据(承认部分答案)。
④ DQ 原则:满载时 D×Q≈常数(容量守恒);故障砍掉 DQ 一块,你选砍在 D(降收获度)还是 Q(降产出率)。
⑤ 复制保收获度、丢容量(产出率);划分保产出率、丢收获度;满载时两者总 DQ 相同,差别只在保哪个量。
⑥ 架构招:拆成正交、独立失败的子系统,把强一致硬状态圈进最小范围,其余用软状态。
⑦ 影响:CAP 定理的直接前身;给「优雅降级 / 最终一致 / 成功率 SLO」以理论名分。
⑧ 局限:harvest / yield 未成通用词;DQ 只是满载近似;CAP 后被指过粗(PACELC 补全);硬性场景不能降 harvest。