Skip to content

算法Algorithm

ABD 寄存器

ABD register · Attiya–Bar-Noy–Dolev register

用单调版本、多数确认与读后写回实现原子寄存器,并以写前查询和二元标签扩展到多写者。

形式陈述 ​

ABD 把原子寄存器实现在异步消息传递系统上。本页先完整说明原始单写多读(SWMR)的无界版本号方案,再在相同故障模型下构造多写多读(MWMR)扩展。

单写多读:模型与协议 ​

SWMR 中,一个指定写者调用 write(v),任意多个读者调用 read();每个客户端上一操作返回后才发起下一操作。固定 n 个副本,至多 f 个发生crash-stop,并要求

n>2f,q=⌊n/2⌋+1.

正确客户端与正确副本之间的请求和响应最终交付,正确进程公平地执行本地步骤、处理每个请求。消息可以乱序,信道不复制消息,也不能伪造或篡改。客户端也可崩溃;终止承诺只针对正确客户端。客户端和副本可以由同一进程承担,但停止服务的副本仍计入 f。没有重启后遗忘状态、动态成员变更或永久隔离的承诺。

每个副本保存一对 (t,v),初始为 (0,v0)。写者私有计数器从 0 开始,每次写严格加一;版本号是无界整数,与实际时钟无关。同一正版本只由唯一写操作产生,且始终携带同一个值。服务器逐个原子处理以下消息:

  • QUERY(id,phase):回复当前 (t,v),并回显请求身份。
  • UPDATE(id,phase,t',v'):若 t′>t,把整对状态替换为 (t′,v′);否则保留原状态。随后总是回复确认,包括版本较旧、状态未变的情况。

每个操作使用不复用的身份,例如 (client,sequence);查询与传播阶段另有不同 phase。客户端只接受匹配本操作、本阶段的回复,并按不同副本身份计数。重复确认只能算一次,旧操作的迟到确认不能凑数。这里的确认表示“处理后版本至少为 t′”,并不表示副本此刻仍恰好保存 t′。

操作流程如下,向全体发送可以由依次发送组成,客户端崩溃可能截断发送前缀。

  1. 写: 写者将计数器加一得到 t,向全部副本发送 UPDATE(id,write,t,v),收齐 q 个不同副本的匹配确认后返回。
  2. 读的查询阶段: 向全部副本发送 QUERY(id,query),等待 q 个匹配回复;选出其中版本最大的整对 (t,v)。
  3. 读的写回阶段: 向全部副本发送 UPDATE(id,readback,t,v),等待 q 个匹配确认,然后返回所选的 v。读者不增加版本,也不在写回期间更改本次返回值。

固定多数下的多写多读扩展 ​

MWMR 允许不同客户端并发写入同一个寄存器,仍要求每个客户端的操作良构。沿用上述固定副本集、正确多数、可靠最终交付和 crash-stop 假设。每个写者拥有互异且来自固定全序的身份 p;身份不重用,进程不重启后重置状态,也不能让两个并行写共用一个身份。操作身份与阶段匹配、不同副本计数和逐条原子处理规则全部保留。

将整数版本替换为标签

τ=(k,p),(k,p)<(ℓ,r)⟺k<ℓ 或 (k=ℓ 且 p<r).

计数 k 是无界自然数,初始标签为 τ0=(0,⊥),其中 ⊥ 仅用于初始化。服务器初态为 (τ0,v0),仍对整对状态进行比较和替换:收到更高标签才更新,但每个合法匹配的更新请求都得到 ACK,包括相等或较低标签。ACK 承诺的是“处理此请求后,本副本标签至少为请求标签”。

MWMR 的读写都执行两个通信阶段:

  1. 写前查询。 写者 p 向所有副本发出 QUERY,收齐 q 个有效回复。设回复中的最大标签为 (m,r),生成新标签 τ=(m+1,p),与本次输入值 v 配对。
  2. 写传播。 向所有副本发送 UPDATE(τ,v),收到 q 个有效 ACK 后返回。各次发送仍是独立动作,崩溃可以截断发送前缀。
  3. 读查询。 向所有副本查询并收齐 q 个有效回复,选择最大标签及其所携带的值 (τ,v)。
  4. 读写回。 将所选整对发送给所有副本,收齐 q 个有效 ACK 后返回所选的 v。读者不生成标签;后来遇到更高并发标签,也不更换这一次已经选好的返回值。

步骤 1–2 属于一次写,步骤 3–4 属于一次读。这里的“双阶段写”是通常称为 MWMR ABD 的直接扩展;1995 年 ABD 原文的 Figure 2 是上面的 SWMR 构造,其多写者结果通过模拟已有共享内存构造得到。后续 Lynch–Shvartsman 工作给出了固定 quorum 的直接双阶段协议并进一步处理动态 quorum。本页只取其中固定多数的思想,按自己的可靠消息接口给出简化协议与完整证明。

直觉

一次写返回,意味着足够多副本已经保存“不早于这次写”的状态。下一次读不必联系同一批副本:任意两个大小为 q 的集合相交,因为 2q>n。这是法定人数系统在证明中承担的工作。

然而,读可能撞见尚未完成的写:某个新值才传播到一个副本,读者就选中了它。此时写者没有建立多数证据。读者若立刻返回,后继读可能完全错过这个值。写回让读者在公开结果前,亲自把“此版本已经被观察到”的证据传播到多数。它帮助尚未完成的写成为后继观察必须尊重的历史。

服务器只接受更高版本,使迟到消息不能撤销这份证据。旧版本请求仍须确认:某个读者正在传播版本 7,服务器已到版本 9,它已满足“至少为 7”的义务;拒绝回复反而可能让该读者无限等待。

多写者增加的难点是:一个刚开始工作的写者,私有计数器可能远远落后于已经完成的读写。ID 可以打破两个并发写者选择同一计数时的平局,却不能让“后来才调用”的写自动排在前面所有完成操作之后。写前查询负责接收多数中已有的标签下界,再把计数加一;读后写回负责让一个已公开的读结果也拥有这样的多数下界。两者共同把客户端之间的实时先后传到服务器状态中。

这与逻辑时钟的标量加身份形式相似,但标签生成的事件不同:此处的计数来自一次 quorum 查询,而非每发生本地事件就加一。标签较小的并发写完全可能较晚返回。证明所需的只是实时先后必然得到相应标签不等式,不需要由标签反推消息因果或墙钟时间。

例子与边界

三个副本上的新旧倒退 ​

令 n=3,f=1,q=2,初值为 0。写者调用 W=write(1),版本为 1,目前只有 A 收到更新,B、C 的更新仍在途中。副本状态为

A=(1,1),B=(0,0),C=(0,0).

若删掉读的写回阶段,R1 查询 A、B,取最大版本后返回 1。等 R1 返回,R2 才调用,查询 B、C,返回 0。两个读都联系多数,而且都可能与尚未返回的 W 重叠;这仍不能成为原子历史:R1=1 要求 W<R1,实时顺序要求 R1<R2,而 R2=0 要求 R2<W,三者成环。

真实 ABD 在 R1 返回前向全体写回 (1,1)。设 A、B 的确认先到,便有 A、B 的版本都至少为 1。此后 R2 查询 B、C 时,B 的回复至少为版本 1,所以 R2 不可能选中版本 0。若 B 在确认后、R2 查询前崩溃,R2 就不能再用 B 的回复凑齐本轮多数,只能由 A、C 回应,其中 A 同样带着证据。

ABD 的读后写回把单点观察变成多数证据:R₁ 收到 A、B 确认才返回,随后 R₂ 从 B、C 查询,B 的版本不能倒退。

从相交证明到完整原子历史 ​

关键不变量有两个:每个副本版本单调不降;每个非初始 (t,v) 都来自唯一写操作,读者只能复制它。假设某传播阶段已得到多数 Q 的确认,传播版本为 t;之后才开始的查询得到多数 Q′ 的回复。取 s∈Q∩Q′。服务器 s 先处理旧传播并确认,再处理后发起的新查询,故新回复版本至少为 t,查询最大值也至少为 t。这里同时使用了集合相交、请求的因果先后和状态单调性。若把以前缓存的回复冒充新查询回复,或允许服务器版本倒退,交集公式便救不了证明。

给每个完成读赋予它选中的版本,每个写赋予自己产生的版本。上面的引理说明:写返回后才开始的读,其版本不小于该写;读返回后才开始的读,其版本不小于前读,因为前读也完成了传播。还有两种实时关系:先写后写的版本严格增加;若一个读返回后才调用某写,该写版本也严格大于读版本,因为读到的版本已经由单写者产生,而新调用尚未发生。

因此可为每个有限历史构造合法顺序:保留全部完成操作,以及作为完成读来源的未完成写;给这些未完成写补上响应,删除其他 pending 操作。将写按版本递增排列,把版本 t 的所有读放在产生 t 的写之后、下一更高版本写之前,同版本读按实时先后排列;版本 0 的读放在初始化之后。四种实时关系都由前述不等式保持,每个读前面最近的写也恰好具有版本 t。这就得到寄存器顺序规格的线性化见证,而不只是“每个读各自合理”。

尤其不能因为写者未收到多数确认,就从历史中删除已经被完成读观察到的写。上例允许把 pending 的 W 补全后排在 R1 前;这是历史的 completion,不是假称真实写者已经返回。

六个操作:迟到确认与崩溃写的来源 ​

仍取三个副本 A、B、C 和 q=2。两位写者身份为 1,2,令

a=(1,1)<b=(1,2)<c=(2,1).

下表用 0 表示初始整对 (τ0,zero),用 a,b,c 分别简记 (a,α),(b,β),(c,γ)。每格都表示服务器处理了本行消息之后的状态;未列出的消息可以继续在途中。读者独立于两位写者。

时刻 操作与本阶段的证据 A B C
0 初始化 0 0 0
1 W1(α) 调用;查询 A、B,均为 0,选择 a 0 0 0
2 W2(β) 调用;查询 B、C,均为 0,选择 b 0 0 0
3 W1 更新 A,并收到 A 的 ACK;其余更新尚未处理 a 0 0
4 W2 仅向 C 发出更新便崩溃;C 收到,写没有返回 a 0 b
5–6 R1 调用并查询 A、C;从 a,b 中选 b a 0 b
7–9 R1 写回到 A、B,收到这两者 ACK,返回 β b b b
10 W1 的迟到更新 a 到 B;B 保留 b 但 ACK,W1 凑齐 A、B 确认后返回 b b b
11–13 R2 调用;查询 A、B 选 b,写回获 B、C 确认,返回 β b b b
14–17 写者 1 调用 W1′(γ);查询 B、C 见 b,生成 c,传播获 A、C 确认后返回 c b c
18–20 R3 调用;查询 A、B 选 c,写回获 A、B 确认后返回 γ c c c

两个初始查询都只看到计数 0,所以第一批写的计数相同,身份决定 a<b。而 W1′ 的查询看到计数 1,故生成 (2,1),它大于 (1,2),不能仅按身份比较。时刻 10 的 ACK 同样值得检查:B 并没有写入 α,但它保存 b>a,所以满足 W1 的传播下界。A 的旧确认也仍然有效,因为 A 此后只会升高标签。

把崩溃写 W2 在数学历史末尾补上响应,得到完整线性化顺序

W1<W2<R1<R2<W1′<R3.

W1 虽然在 R1 之后才实际返回,两者却重叠,允许如此排序。W2 的写值已经被完成的 R1 读出,必须保留来源写;补全响应并不声称崩溃客户端真的恢复返回。表中三个读分别读到最近前驱写的 β,β,γ,而 W1 的值可以在没有读返回它的情况下被下一写覆盖。

响应先后不等于标签顺序:R₁ 写回 b 后返回,迟到的 a 仍获确认。圆点表示实际返回,叉号表示写者崩溃,W₂ 的虚线表示始终未返回;仅在数学历史中补全 W₂,便得到下方合法顺序。

MWMR 的不变量与完整线性化证明 ​

同一标签只携带一个写值 ​

服务器标签单调不降,直接来自 UPDATE 只接受严格更大标签。每个正标签最初由一次写产生,读者只转发原有整对;因此只要证明不同写不会生成同一标签,就能归纳得出“同一标签永远对应同一值”。不同身份的写有不同第二分量。对同一身份的两次连续写,前次已经返回才会调用后次;前次确认多数与后次查询多数相交,交点在确认时已达到前次标签,以后不会降低。因此后次查询得到的最大计数不小于前次计数,加一后严格更大。跨越任意多次同身份写也同理。初始标签由初始化单独提供,不会被普通写生成。

这也解释了相等标签 UPDATE 不必替换值:相等标签的合法消息携带相同值,保留原状态不会丢失另一项写入。该结论依赖消息不能伪造、同一写者不并行复用身份,而不是仅靠字典序的定义。

多数如何把下界传给下一次查询 ​

设某操作的传播阶段以标签 τ 收到多数 P 的确认,随后该操作返回;另一操作在它返回后才调用,查询阶段从多数 Q 获得回复。取交点 s∈P∩Q,有如下消息先后链:

s 处理 UPDATE 并发 ACK<前操作返回<后操作调用并发 QUERY<s 处理此 QUERY.

因此 s 的新回复至少含标签 τ,查询选出的最大标签也至少为 τ。匹配操作身份保证这里确实是新查询的回复。若交点在处理本轮 QUERY、发出回复之前已经崩溃,它便不能贡献本轮回复;若它发出回复后才崩溃,已发出的匹配回复仍可计入 Q,发送时的标签下界已足够支撑证明。因此,证明并未要求旧多数永远全部存活。

给每个完成读赋其选中标签,给每个已经生成标签的写赋其新标签。若 o1 返回后 o2 才调用,上述引理得到全部四种实时关系:

先完成的操作 后调用的操作 标签关系与理由
写 W 读 R τ(W)≤τ(R);读查询继承写传播下界
读 R 读 R′ τ(R)≤τ(R′);后读继承前读写回下界
写 W 写 W′ τ(W)<τ(W′);后写查询继承下界,再增加计数
读 R 写 W τ(R)<τ(W);写查询继承读写回下界,再增加计数

最后一行正是多写者情况下不能借用“唯一写者早已生成该版本”来证明的地方:承担这项工作的机制变成了写前查询。

从标签构造整个历史的顺序见证 ​

取任意有限历史,保留全部已完成操作。对每个读到非初始标签的完成读,再保留生成该标签的唯一来源写;若来源写 pending,就在历史末尾给它补上响应。删除其余 pending 操作,包括未完成读。来源写可能只传播到一个副本便崩溃,这不会改变上述选择。

将所有保留写按标签严格递增排列。每个读放在同标签来源写之后、下一更大标签写之前;初始标签的读放在所有普通写之前。同标签的读按原实时偏序的任意线性扩张排列。因为写标签唯一,这条规则不会在两个同标签写之间发生歧义。

检查实时顺序即可确认构造有效。不同标签操作的实时边由四种不等式保持;同标签读之间的边由线性扩张保持;同标签的写在读之前,而该读不可能在来源写调用前就返回,因为其标签尚未产生。补全的写没有原始返回,所以不会凭空产生一个必须排在后来调用之前的返回事件;但若某完成操作先于来源写调用,其标签关系仍由相同查询引理约束。这样所有原实时边都保留,每个读的最近前驱写又恰好是同标签来源写,满足线性一致性的顺序寄存器规格。

这是整个历史的线性化证明,不必把每次查询中某条消息的处理瞬间指定成固定线性化点。特别是并发写的标签顺序可以与响应顺序不同,只要没有倒置非重叠操作的实时边。

三处删减分别破坏什么 ​

只用私有计数器加身份,删去写前查询。 让身份 2 的首写以 (1,2) 写入 β,更新全体并完成。随后身份 1 才进行自己的首写,以私有计数产生 (1,1) 并写入 α。所有副本都保留较高的 (1,2),但仍正确确认旧更新,于是第二写完成。再调用读,它返回 β。三个操作不重叠,顺序规格却要求读到最近写的 α。两个标签一直唯一;失败的是标签没有保持实时先后,不能靠 ID 打破平局修复。

删去读后写回。 让一个新写只把 (a,α) 送到 A 就崩溃,B、C 仍为初态。R1 查询 A、B 返回 α,之后调用的 R2 查询 B、C 返回初值。这正是前面 SWMR 新旧倒退反例在二元标签下的同一机制:任何 completion 都必须同时满足 W<R1<R2<W,无法消除环。写者拥有多少身份不改变读结果公开前需要传播证据的事实。

仅在标签真正替换时 ACK。 某低标签传播仍在途中,更高并发写先更新全部正确副本。低标签请求到达后没有任何副本替换状态,若因而都不 ACK,正确客户端就永远等不到多数。单调性仍在,却失去了终止;相等标签也应确认,否则读者把刚查到的标签写回原副本便可能被无谓阻塞。

推论与应用

终止与通信成本 ​

至少 n−f≥q 个副本正确。正确客户端逐一发出请求后,这些副本最终收到、处理并响应;因此 SWMR 写的一个传播阶段、两种读的查询与写回阶段,以及 MWMR 写的查询与传播阶段都能完成。并发更新不会使等待条件失效,因为较旧更新也收到确认。这个终止论证不需要辨认谁崩溃,更不需要给消息延迟设超时上界。

在不重传的上述可靠消息接口下,每阶段最多 n 个请求和 n 个响应,SWMR 写最多 2n 条消息,两种读与 MWMR 写均最多 4n 条消息,均为 O(n);迟到响应也计入其所属阶段。每个副本存一对版本和值,客户端还需至多 O(n) 个回复身份及查询结果。版本号随写次数增长,因此这不是固定比特空间算法;k 次写后的版本字段需要 O(log⁡(k+1)) 比特,消息还包含值和请求身份。两个通信阶段也不等于两段有界墙钟时间:异步模型只给最终完成,没有统一响应时限。

MWMR 标签还包含写者身份,且请求身份不能复用;本页没有提供固定比特空间的循环回收办法。若 n≤2f,本协议无法同时满足严格多数相交和任意 f 次崩溃后的可用性。永久隔离在少数分区中的客户端也不能完成这些阶段,这超出了可靠最终交付的前提。

寄存器接口的边界 ​

ABD 说明可靠消息、正确多数和读后写回足以构造读写对象;写前查询使这套证据传递进一步覆盖多写者。它没有实现任意对象上的原子读改写,也不把多个寄存器的一组读自动变成快照。本页的一阶段写依赖唯一写者,双阶段写则允许多个良构客户端并发写;有界版本、恢复和重配置仍各需独立协议。

自测:在三副本例子中,把 R1 的写回确认集合改为 A、C,随后 R2 查询 A、B,会从哪个交点继承证据?若写者在只更新 A 后崩溃,正确读者还能完成吗?检查标准:交点是 A;只要正确副本仍不少于两个且通信最终交付,读者可以传播已见到的合法版本并完成,不必等待原写者复活。

参考资料
  • Hagit Attiya, Amotz Bar-Noy, Danny Dolev, “Sharing Memory Robustly in Message-Passing Systems”, Journal of the ACM 42(1), 1995, pp. 124–142。§2.1 的模型、§3 的通信原语、§4 Figure 2 与 Lemmas 4.3–4.6、Theorem 4.9 给出无界单写多读构造;§5 的有界版本是另一构造。本文显式携带值、用操作身份区分消息,以固定严格多数表述其核心协议。
  • Nancy Lynch and Alex Shvartsman, “Robust emulation of shared memory using dynamic quorum-acknowledged broadcasts”,1996-12-02 作者版,§4.1(印刷页 8–10)的固定 quorum 算法、§4.2 的原子性结论及 Appendix B 的 Lemmas 4.2–4.4 证明。这里引用作者版定位,不以其分页代指 1997 会议短版;本文也不采用其动态配置、重启和底层广播接口的完整模型。
  • James Aspnes, Notes on Theory of Distributed Systems,2026-04-25 版本,§§17.2–17.5,印刷页 144–148;§17.5 给出写前多数查询与二元标签扩展。
关系图谱11 个相邻概念 · 3 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系