Skip to content

算法Algorithm

Chord指表查找

Chord finger routing · Chord lookup · Chord指表

在固定成员和正确后继条件下用局部幂二指表求键的负责节点,以目标前驱距离证明跳数,并展示陈旧指表与错误后继的不同后果。

形式陈述 ​

已知放置公式,但每台机器没有整张表 ​

一致性哈希环规定键k归顺时针第一个坐标≥k的节点。若所有客户端都有完整有序成员表,可以本地二分;Chord解决另一项任务:在消息传递模型中,每个节点只持有少量指针,通过转交查询求同一个后继答案。[1, §IV.C–D]

本页固定m≥1、M=2^m,以及非空、坐标互异的稳定成员集。查找期间成员不加入、退出或失败,消息可靠且最终交付。每个节点u的successor必须是全环紧接u的下一成员;唯一成员的successor指自己。返回的是负责节点身份,不包含取值、复制、事务或成员维护。

完整指表的第i项为

fingeru[i]=successorKey((u+2i)modM),0≤i<m.

这里successorKey是“不小于目标”的环后继,节点自己的successor则是“严格在自己之后”的成员后继;在唯一成员情形二者都回到自己。不得在多成员环把successorKey(u)=u误当作节点的下一成员。

每一步只读取当前节点的状态 ​

定义顺时针距离δ(a,b)=(b−a) mod M,值在0,…,M−1。节点u处理查询k时:

  1. 若k=u,返回u;若successor=u,在正确后继前提下整环只有这一成员,返回u
  2. 若0<δ(u,k)≤δ(u,successor),返回successor
  3. 否则从指表里选满足0<δ(u,v)<δ(u,k)的节点v中,δ(u,v)最大的一个,向v转交查询;没有这样的指表项时转交正确successor

步骤3不把越过k、指回自己或落在反方向的指针当作进展。完整指表第一项本来就是successor;显式后备分支用于后文的不完整/陈旧指表。结果沿调用链返回,或由等价的迭代查询驱动器继续下一跳。

直觉

后继保证不漏成员,长指针负责少走路 ​

只沿正确successor也能找到答案:每次走到环上下一成员,直到当前节点与后继把k夹住。它可能访问几乎全部N个成员,但不会跳过真正owner。

幂二指表加入更远的捷径。节点知道自己前方1、2、4、8……个坐标处的首个成员,可以从这些指针中挑仍位于目标之前且最靠前的一个。指表记录的是成员,不是那些幂二起始坐标本身;许多起始坐标可以落到同一个成员。

减半应测到目标前驱,而不是随意测到k ​

令p为k之前最后一个成员,不把恰等于k的成员算入此前驱。只要当前节点u还未能返回owner且u≠k,p就在u到k的开区间上;转交节点总位于u之后、p及之前,因此不会绕过p。

若u≠p,记D=δ(u,p)>0,选择i使2^i≤D<2^(i+1)。因为p本身是一个成员,完整指表的f=successorKey(u+2^i)必落在从u+2^i到p的闭区间,因而f是合法前进候选。实际所选v至少和f一样靠前,所以

δ(v,p)≤D−2i<D/2,

D恰为2^i时新距离为0。于是至多m次转交到达p,再由正确successor返回owner。这里分析的是到成员p的距离;例如成员0、1、16,u=0、k=15时,第一步只能到1,到k的数字距离从15变14,并没有减半,但下一步已经可以返回16。

原论文的O(log N)高概率结论还使用成员位置的随机分布;本页对任意固定合法m位成员布局证明的是O(m)确定跳数。不能仅因一张指表有幂二偏移,就在任意稀疏或对抗布局上宣告O(log N)。[1, Theorem IV.2]

例子与边界

m=5时的整张局部指表 ​

空间0,…,31,成员为1、4、8、14、21、28。每列起点依次为u+1、u+2、u+4、u+8、u+16,按32绕回。

u finger[0] finger[1] finger[2] finger[3] finger[4]
1 4 4 8 14 21
4 8 8 8 14 21
8 14 14 14 21 28
14 21 21 21 28 1
21 28 28 28 1 8
28 1 1 1 4 14

从4查31:4的最大合法前进指针为21;21的最大合法前进指针为28;28发现31落在(28,1]的绕回区间,返回owner1。实际处理查询的节点依次4→21→28,不需要让1再处理一次“求身份”请求。

从1查27,1→21后返回28;从28查0,立即返回1;从14查14,直接返回自己;从8查3,路径8→28→1,最后返回4。后两例分别检查相等和越过零点的区间规则。

指表变短,与后继错误是不同故障 ​

保持成员和所有successor正确,只让每份指表剩自己的successor。从4查31会走4→8→14→21→28,答案仍是1,跳数变多。更一般地,只要候选指针仍指当前活成员且严格位于(u,k),就保持前进;没有捷径时走successor,最多沿N个成员处理,可能退化到O(N)。此时不再具有完整指表的减半保证。

反过来,把28的successor错误设成4,却仍让成员1留在环内。从28查0会发现0∈(28,4]而返回4,漏掉真正的owner1。即使所有转交看起来都“向前”,错误的终止区间也会制造错误答案。因此正确successor是本定理的条件,不能拿指表搜索结果反过来当作它已经正确的证明。

若一次转交的地址不可达,协议尚未完成。有限超时不能证明该节点已退出,也不能让调用方擅自改成“我目前能联系到的第一个就是owner”。下载器把这种事件和跳数预算耗尽记为UNKNOWN,保留已走路径;这是观察出口,不是成功的成员重配置。

推论与应用

通信跳数与本地扫描分开计 ​

每个节点保存m项指表和一个successor,状态O(m)。本页透明实现逐项扫描求最大的合法前进指针,每个处理节点O(m)整数比较;完整指表至多O(m)个处理节点,故本地总工作O(m²)、消息转交数O(m)。不能把跳数直接当作处理器总工作。

若指表只有s_u项,一条访问路径上的本地扫描工作为O(Σ(1+s_u));保存h步完整候选日志需O(Σ(1+s_u))输出,不只是h个节点名。简化追踪只保存选中节点和终止理由时才有O(h)日志。下载器的故障实验还把F个不可达地址建成集合,预处理另需期望O(F)时间和空间;它不属于节点本地查找决定。

核验器用一份完整成员表离线生成标准指表,再通过只读当前节点局部状态的查找器执行。这张全表是测试初始化与独立oracle,不是查找器在每跳偷偷读取的共享目录。批量建N份指表用N m次全表后继查询,另付排序和O(Nm)存储;真实系统怎样维护这些表属于另一协议。这里按整数比较和节点索引常数访问计费,Python字典/集合为期望成本,大整数位运算另计。

稳定查找证明不承担动态修复 ​

节点加入、离开和失败会使successor也变旧,需要维护算法及其不变量。Zave明确指出原始Chord维护规格在原假设下并不保证所声称的最终可达性,并给出修正规格与证明。[2, §I–II] 本页没有实现该维护协议,因此不从静态查找推出“任意并发加入和故障都会自动收敛”。

放置与查找终点分别验收完整指表、只有后继的降级状态、错误后继反例及不可达出口。能返回一个节点号,还必须检查它等于当前冻结成员表定义的后继;而位置正确之后,数据是否已迁移仍回到放置单元的另一份责任。

参考资料
  1. Stoica、Morris、Liben-Nowell、Karger、Kaashoek、Dabek、Balakrishnan,Chord: A Scalable Peer-to-peer Lookup Protocol for Internet Applications,2003作者稿,§IV.C–D、Figure5、TheoremIV.2,PDF pp.4–6:后继查找、局部指表和到目标前驱的距离论证。本文0基指表、相等出口、有限预算及错误后继反例为明确教学接口。
  2. Pamela Zave,Reasoning About Identifier Spaces: How to Make Chord Correct,2017,所读2019修订稿v2,§§I–II:原维护协议的正确性问题、理想环和维护模型。修正维护仍有通信、存活后继与初始化条件,不能被本页的静态查找实验代替。
关系图谱7 个相邻概念 · 1 类关系

拖动节点调整位置。

显示关系

显示:依赖

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