分布式哈希表原理与实现(Python版和Java版)

 

 

本文介绍了布隆过滤器的概念及变体,这种描述很是适合代码模拟实现。重点在于标准布隆过滤器和计算布隆过滤器,其余的大都在此基础上优化。文末附上了标准布隆过滤器和计算布隆过滤器的代码实现(Java版和Python版)node

本文内容皆来自 《Foundations of Computers Systems Research》一书,本身翻译的,转载请注明出处,不许确的部分请告知,欢迎讨论。算法

 

什么是分布式哈希表?

 

    分布式哈希表是一个基本的结构,被普遍的应用在许多分布式系统中,用于组织动态改变的(分布式)节点集合和透明的资源定位服务,如,DHT 在许多 P2P 系统中做为一个基本结构提供高效透明的资源定位服务。全部节点都同等重要,这使得 DHT 更加的装载平横。并且,在 DHT 中具备必定规律的拓扑结构(环、树、超立方体、网格、蝴蝶形……),这使得查找更加的高效。虽然,各类 DHT 实现的拓扑结构各不相同,但它们至少提供如下两个基本的功能:
  • put(key, value): 插入一个键值对到 DHT 网络中,不须要知道具体的存储地点
  • value = get(key):根据 key 从 DHT 网络中检索相应的值,不须要知道具体的存储地点

 

DHT 网络设计的重点大多在如下几个方面:
  • 拓扑结构(Topology structure)
  • 路由协议(Routing protocol)
  • 动态维护和失败恢复(Dynamic maintenance and failure recovery)

 

 

各类 DHT 对比

DHTs Topology Node Degree Routing Hops
CAN mesh O(d) O(dn^(1/d))
Chord ring O(logn) O(logn)
Pastry tree + ring O(logn) O(logn)
Tapestry tree O(logn) O(logn)
Kademlia tree O(logn) O(logn)
Viceroy butterfly 7 O(logn)
Koord de Bruijn graph + ring 2 or O(logn) O(logn) or O(logn/loglogn)
Cycloid hypecube + ring O(d) with n = d2^d

 

一个 DHT 例子 —— Chord


Chord 的拓扑结构

  • Chord 使用一致性哈希(consistent hashing)将键(key)和节点(node)映射成一个 m 位的标识符
  • Chord 使用环形做为它的拓扑结构,节点按照顺时针,标识符递增排列。所以,每个节点都有一个直接前驱和一个直接后继。
  • 每个节点负责存储标识符范围为(i-1, i] 的键。i 为节点的标识符,i-1 为当前节点前驱节点的标识符。
拓扑结构如右图。N14 节点负责存储标识符为(9,10,11,12,13,14)的键
 
             

Chord 的查找算法

    如果使用普通的遍历查找,那么每一个节点的度为1(只有后继),查找的时间复杂度为log(n)。而 chord 在节点度数和时间复杂度之间作了个折中:增长节点的度,减小查找时间。
    每一个节点都有一个 finger table,该表有 m 个条目,每一个条目指向另外的节点。条目指向的节点计算公式为:(n + 2^k) % 2^m 。(n 为当前节点的标识符,m 是标识符空间的位数,0 <= k < m)。如右图。
    为何要设置这样一个指向表呢?为了更快的查找。假设如今咱们要从节点 n 开始,查找标识符 id。因而有以下的步骤:
  • 计算当前节点 n 到目标节点(存储 id)的距离:d = (id-n+2^m) % 2^m,从这里能够知道:(n + d) % 2^m = id
  • 而后计算最大的 k,使得 2^k <= d。而后根据 finger table,跳转到存储标识符 n + 2^k 的节点。如今离目标又近了一步
  • 以上过程递归执行,直到目标标识符位于当前节点和其直接后继之间,那么此时目标标识符就存储在当前节点的直接后继中
 


Chord 的动态维护和失败节点恢复
网络

    在分布式系统中,会常常的进行节点的动态加入和离开,并且节点也可能出问题,因此还须要考虑节点的动态维护,一些算法就要周期的执行来维护拓扑结构。举一个例子,节点 N26 加入该网络:并发

  • 节点 N26 向节点 n 请求加入网络
  • n 会找到为标识符 26 负责的节点 N32,N32 就是 N26 的直接后继
  • 节点 N26 会修改本身的后继,指向 N32,而后执行一个算法更新后继,而且给新后继 N32 发送一条问候信息
  • N32 收到该消息后,发现 N26 离本身比当前前驱 N21 更近,因此,N32 就会重设它的前驱为 N26
  • N21 执行一个算法更新后继,发现 N26 是本身的新后继,因而修改指向,并发送问候信息给 N26
  • N26 收到消息后,发现本身的前驱为空,而后重设本身的前驱为 N21
上面的过程可以很好的处理节点的加入,但当节点出了问题时,就很可贵知其后继节点信息,所以,Chord 还维护了一张后继信息表,但不是记录全部节点的,表长为 r = log(N),当节点出问题后,可以有较大几率获取正确的后继节点信息。
 

学习案例(待更新)


案例一:协同域名系统(Cooperative Domain Name System,CoDoNS)




案例二:协同文件系统(Cooperative File System,CFS)



 

代码实现(待更新)

相关文章
相关标签/搜索