09 / READ THE ORIGINAL PAPER
论文为Seth Gilbert与Nancy Lynch的Brewer’s Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services 。以下解析依据链接中的12页PDF,页码均按PDF页序计算。先读前面的两副本例子,再读本节的执行构造,便能把“CAP三选二”还原成有假设、有量词、有反证过程的理论结论。
核心结论: 在允许网络分区的模型中,不存在一种读写算法,能够对所有允许的执行,同时保证线性一致性和可用性。限制针对算法能兑现的承诺,不取决于机器是否足够快,也不要求节点宕机。
9.1 阅读地图:不只有一个CAP证明
PDF位置 原文内容 阅读时要回答的问题
第2—4页 · §2 原子读写对象、可用性、分区容错的形式化定义。 C究竟是哪一种一致性?A是否有固定响应时限?哪些故障被模型允许?
第4—5页 · 定理1 纯异步模型中的不可能性证明。 两种执行为什么对读取侧不可区分?矛盾发生在哪里?
第5—6页 · 推论1.1 即使只要求无丢消息的执行保持原子一致,也有额外限制。 “消息永久丢失”和“消息只是很慢”能否区分?
第6—7页 · §3.2 分别保留两种性质的简单构造。 每个构造依赖什么假设,放弃了哪项保证?
第7—8页 · 定理2与§4.3 有时钟和已知时间上界时,不可能性仍成立,但能提供不同的降级保证。 知道通信超时,是否等于知道远端数据?
第9—11页 · 定义3与定理4 Delayed-t一致性及中心节点协议的构造。 恢复后多久、以什么条件,重新约束跨节点操作顺序?
9.2 研究对象与模型:一个变量就足够暴露限制
研究对象是可读写的数据对象:初始值为v₀,客户端执行write(v)与read()。至少两个节点通过消息共同提供这个对象;客户端可向不同节点请求。这里不需要SQL事务或复杂业务。一个简单读写对象已经足以证明这些承诺不能同时实现,更多业务代码不会补足被分区切断的信息。
C是原子一致性,也称线性一致性。 全部操作必须能安排成一个合法的总顺序,看起来各自在调用与返回之间的某个瞬间生效,并尊重实际时间先后:写完成之后才开始的读,必须返回该值或更晚写入的值。时间重叠的操作则可以选择合法顺序。它不要求所有物理副本每一瞬间都相同;内部传播可以有延迟,但客户端观察必须满足这一规则。
A要求每个非故障节点收到的请求最终完成。 论文不规定多少毫秒内完成,因此这里的A既不同于响应延迟SLO,也不同于年度在线率。但不能永久等下去,或以任意错误响应替代成功完成的原读写操作。读写返回还必须与所承诺的对象语义结合讨论。
P通过允许任意数量的消息丢失来建模。 跨分区消息可以永远到不了,分区内部节点却可以正常工作。论文明确不研究节点停止故障;证明只需要通信失效。定理1讨论公平执行:不能靠让一个正常节点永久得不到运行机会来制造反例,但公平调度不意味着跨分区丢失的消息必须被送达。
异步模型中没有时钟,节点根据收到的消息和本地计算决策。模型没有提供可用于区分“消息丢失”和“消息迟到”的有限通信时间上界。上述假设是后面证明能成立的条件,不能省略后只剩一句“分布式系统不可能一致”。
原文:§2定义,第3页 ;异步模型与定理1,第4页 。
9.3 定理1:用三个执行,证明算法不存在
反证法先假设存在算法𝒜,能满足原子一致性、可用性和分区容错。将网络划成两个不相交、非空的节点集合G₁与G₂,两集合之间的消息全部丢失。设置初始值v₀=0,新写入值v₁=1。
执行 请求与通信 由假设推出的结果
α₁:只写 G₁接到write(1),两侧没有其他客户端请求,跨分区消息均未到达。 A要求写在有限的执行前缀内完成;不能等待另一侧到无限久。
α₂:只读 没有任何写入,G₂接到read(),跨分区消息均未到达。 A要求读取完成;C要求没有写入时返回初始值0。
α:先写后读 先运行α₁直到写完成,再运行α₂的读取过程,跨分区消息继续丢失。 G₂的观察与α₂相同,所以读取仍返回0;但C要求它返回1。
对照世界α₂ 没有发生写入 G₂未收到G₁消息 read()必须返回0
组合世界α G₁的write(1)已完成 G₂仍未收到G₁消息 随后read()按C必须返回1
图4 · 两个世界对G₂的本地观察可以完全相同,却要求不同的正确答案;这是不可区分性矛盾。
α₁没有给G₂引入任何客户端请求,也没有送达来自G₁的信息。可以安排G₂看到与α₂相同的本地事件。因此对G₂而言,组合执行α与只有读取的α₂不可区分。算法不能根据完全相同的输入与本地状态,凭空判断另一个世界是否完成了写入。
但组合执行里,写入已返回,读取才开始,二者没有时间重叠,不能把读取任意排列到写入之前来解释旧值。G₂返回0违反C;无限等待违反A;猜测返回1则在根本没有写入的α₂里违反C。因此最初假设的算法不存在。
量词是证明力量的来源。 定理不是说某一种主从方案失败,而是说任何想兑现全部承诺的算法,都存在一个模型允许的执行使承诺不能同时成立。反例只需发生一次,就足以否定“对所有允许执行都保证正确”的声明;它不表示普通网络运行时每一次请求都会遇到这个问题。
原文:α₁、α₂与α的正式构造,第5页 。
9.4 推论1.1:“最终没丢消息”不等于“消息足够及时”
尝试把要求放宽:所有公平执行中保持可用;只有在所有消息最终都送达、没有丢失的公平执行中,才保证原子一致;真正丢消息时允许旧值。推论1.1指出:在纯异步模型中,这组保证仍不能同时实现。
证明从定理1出发。一个始终保证A的算法,必然存在允许丢消息的公平执行α,在某个有限时刻返回违反原子一致性的结果。截取到这个结果为止的有限前缀α′,再构造另一个执行α″:保留此前相同的可观察事件,把尚未到达的消息解释为延迟,并在此后全部送达。
α″最终没有丢消息,但错误响应已经出现。把消息在之后补送,不能改写客户端已经观察到的操作历史。因此算法违反了它对“无丢消息执行”的原子一致承诺,假设再次矛盾。
这里构造的是另一个执行。 不是把原执行里真正丢失的消息复活,而是让同一个有限观察前缀出现在“所有消息最终送达”的执行中。纯异步节点在响应时无法区分这两种未来。
这个结果不否定异步复制的最终一致性。被否定的是更严格的组合:分区时请求始终完成,同时所有最终无丢消息的执行从头到尾保持线性一致。消息后续抵达可以帮助收敛当前状态,却不能把过去已经返回的旧读变成线性一致。
原文:推论1.1,第5—6页 。
9.5 “任意两个可实现”的构造,分别放弃什么
第3.2节不是只给口诀。C+P的例子是指定中心节点维护权威值,其他节点转发请求,收到确认再回复;通信失效时请求可能不完成。C+A可以在排除分区且消息送达的假设下采用同样的集中处理。A+P则可以放弃原子一致性,极端例子是读取总返回初始值,真实系统可进一步定义更有价值的弱一致性。
因此,AP标签本身没有规定旧值最多落后多久,也没有承诺业务最终正确;CA的构造排除了分区条件,并没有创造“永远不会分区”的网络。CP也不要求所有节点一起停止服务,只是不能要求所有非故障节点的每个请求都能继续完成。
原文:§3.2,第6—7页 。
9.6 定理2:时钟能识别超时,不能创造远端信息
第4节加入更强的时间假设。每个节点有时钟,走速相同,但显示值不要求同步;消息要么在已知时间t_msg内到达,要么丢失;收到消息后的处理也有已知上界t_local。这里应按论文自己的定义理解“部分同步”,不要将它直接等同于所有其他文献中的同名模型。
定理2说明:即使有这些计时能力,只要允许任意消息丢失,仍不存在对所有执行同时保证A与原子一致性的读写对象。知道对方无法通信,并不等于知道对方有没有执行写入。
证明仍然构造G₁完成写入、G₂随后读取的分区执行。但这次时钟可观察,不能直接忽略经过的时间。作者在“只有读取、没有写入”的对照执行前加入长空闲区间,至少覆盖写入执行的时长,再将两个执行叠合。这样既保证写完成后读才开始,也保留G₂无法区分两个世界的条件,得到同样的旧读矛盾。
原文:模型与定理2,第7—8页 。
9.7 为什么时间上界能改变降级保证
定理2仍成立,但推论1.1在这个模型中不再成立,因为模型已提供判断通信是否按时完成的能力。节点可先请求中心节点,等待2 × t_msg + t_local:请求去程、处理与回复回程。及时收到回复就用中心结果;超时则返回本地已知值或确认请求,接受原子一致性可能被破坏。
在全部消息按模型上界送达的执行中,可以保持原子一致性;丢消息时采取较弱的响应策略。这项能力来自模型增加的假设。生产系统设置500毫秒超时,只是选择等待预算,不能由此证明“凡是没丢失的消息都在500毫秒内到达”。网络拥塞、队列积压和进程暂停均可能使工程超时出现。
尤其要区分写请求的确认含义。论文讨论弱化的读写对象保证,不是在证明一个超时确认已经同步到全部副本,更没有证明它已经完成生产系统要求的持久化与业务副作用。
原文:§4.3,第8页 。
9.8 Delayed-t一致性:恢复约束精确到操作顺序
定义3给出比“最终会好”更具体的弱一致性条件。它要求存在操作偏序P:所有写操作有序,所有读操作相对于写操作有序;每次读返回其在P中前一个写的值,没有前序写则返回初始值;P还要尊重各节点提交请求的顺序。这里的偏序P是数学记号,不是CAP中的分区容错P。
除此之外,它规定两种跨节点时间约束:整个执行的消息都正常送达时,保留原子一致性的实际时间顺序;发生丢消息时,若操作θ已经完成,随后存在一个长度大于t、没有消息丢失的区间,操作φ在这个区间结束后才开始,那么φ不能在P里排到θ之前。
操作θ完成
↓
正常通信持续超过t,期间没有消息丢失
↓
操作φ开始
↓
φ不能被解释为发生在θ之前
简单例子:没有其他写入时,write(1)已完成,随后网络正常超过t,再发起read(),后者不能仍表现得像这次写入从未发生。这个例子帮助理解定义,但完整条件是对读写操作顺序的约束,而不是一个不附条件的“数据只会落后t秒”指标。
第10—11页的协议变体使用中心节点C分配递增序列号,节点读取中心值或已缓存的最高已知序列号;写入与更新广播在超时后继续重试。原文将写重试等待约束为t − 4 × t_timeout以内,将中心重发等待约束为t − 2 × t_timeout以内,以安排恢复窗口中的传播;参数选择需让这些等待预算有意义。
定理4的证明把写操作按中心获知它们的顺序排列,把读放到其所返回值对应的写之后;再论证一段足够长的正常通信区间能让此前更新经中心传播,从而约束区间结束后开始的操作。这里是论文给出的协议与证明思路,不应直接复制成处理并发写、去重、节点崩溃和持久化的生产协议。
它不保证无限分区期间的数据同步,不保证网络刚恢复的一瞬间所有读就线性一致,也不提供库存不超卖、资金守恒等业务不变量的证明。现实系统的恢复时限还依赖重试、负载、积压和故障假设,不能直接把论文的t当成自己的SLA。
原文:定义3,第9页 ;协议构造,第10页 ;定理4,第11页 。
9.9 结论边界:论文没有替我们做业务设计
它没有证明所有分布式系统在正常通信时也必须放弃C或A;限制针对包含分区的全部允许执行。 它没有证明CP意味着整个集群停机,或AP意味着可以任意返回错误数据;可服务范围与弱一致性语义仍需定义。 它没有证明BASE和最终一致性自动保证业务正确;副本收敛与业务约束成立是两个问题。 它没有证明超时等于失败;远端可能执行成功而回复丢失。 它没有证明某个数据库、注册中心或消息系统整体属于CP或AP;必须分析具体操作、协议、读取路径和故障模式。 它没有在本论文的模型中解决节点停止故障、业务事务、持久化与外部副作用;这些需要额外机制与证明。
“另一侧完成了写入”和“另一侧没有写入”,对当前节点可能完全不可区分,但线性一致性要求两个世界给出不同答案;可用性又不允许无限等待。CAP证明的是这种信息约束。工程设计随后要决定允许哪些操作等待、哪些观察暂时落后,以及满足什么条件才能恢复承诺。
闭卷自测:能否不看原文,复现证明? 画出G₁、G₂与初始值;构造只写α₁、只读α₂、先写后读α;指出A在哪两处保证完成,C在哪两处要求不同值;说明G₂为什么不可区分。再解释推论1.1为何构造“延迟后送达”的另一个执行,以及定理2为什么要加入空闲时间。最后写出Delayed-t恢复条件,而不只背诵“最终一致”。