分布式系统中的算法设计(一) -- 一致性 Hash

原创 2016年02月25日 11:05:02

Hash 大家都知道,把某个要存储的内容的索引 key 通过某个规则计算一下,算出来一个值,这个值往往范围比原来小,且概率意义上不会冲突。

由于 Hash 计算复杂度往往比查找要快,被大量应用到各种大规模的系统中,特别是分布式系统。具体实践中有几个典型的问题。

问题来源

一致性 Hash 讨论地已经很多,基本故事就是分布式存储系统中,通过 Hash 来决定内容存到哪个节点上。

典型的比如 cache 系统,后面放若干节点,查找某个 key,hash 到某个节点,再进一步检索。

最简单的自然是按照节点数量取个模,理想状态下不会有啥问题。

但是如果发生如下情况,就无法正常工作了。 加节点(一般是有计划的) 减节点(可能突然故障,无法预知)

所以提出了一致性 hash,即考虑如下四个方面的算法: 平衡性(Balance):哈希结果尽可能分布到所有的节点,这样可以使得所有的节点都得到利用; 单调性(Monotonicity):加入新的节点后,已经分配节点的 hash 结果 被映射到原有的或者新的节点上去,而不会被映射到其他节点; 分散性(Spread):同样内容尽量避免 hash 到不同节点; 负载(Load):不同内容避免 hash 到同样节点。

简单的说,就是有一个算法,首先分配尽量均匀,其次,当节点个数变化的时候,尽量维持原来内容的映射,并进行局部调整。

能满足的算法有很多,这里介绍两个比较经典的。

基于环的实现

该算法最初于 1997 年在论文 Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web 中提出。算法复杂度为 O(log(n))。

主要设计思想为:构造一个环(顺序标上编号比如 0 ~ 2^32-1),将已有节点编号并分配到这个环上,分割环为不同的段。

对每个内容,hash 后是一个独一无二的数字,这个数字肯定会落入某个段上,按照固定方向往前找到的第一个节点编号即为要分配到的节点。

考虑两个相邻的节点 A 和 C,如果要在中间添加一个节点 B,那么 AB 段上内容会从 C 节点重新分配到 B 节点。反之,如果删除掉 B 节点,那么 AB 段上内容会再次分配到 C 节点上。

这种算法很好的满足性质 2-4,唯独平衡性没有解决。即当节点个数不多的时候,内容可能会集中在某些段,划分到部分节点上。要解决这个问题也很简单,把一个节点虚拟为多个虚节点,提高整体节点个数,然后再分散放到环上。这样就提高了平衡性。

基于概率转移的实现

该算法最初于 2014 年在论文 A Fast, Minimal Memory, Consistent Hash Algorithm 中由 Google 工程师 John Lamping 和 Eric Veach 提出。算法复杂度为 O(log(n))。

主要设计思想为:假设原先有 n 个节点,新加上一个节点后,每个 key 都有 1/n+1 的概率转移到新的节点上,n/n+1 的概率留在原先分配的节点。

举个例子,原先有 1 个节点,再加一个节点,那么已有内容有 1/2 的概率跳转到到第二个节点。原先有 2 个节点(每个里面有 1/2 的内容),再加一个节点,则已分配内容应该有 1/3 的概率转移到新的节点。

一个简单的实现代码为:

int ch(int key, int num_buckets) {
    random.seed(key) ;
    int b = 0; // This will track ch(key, j +1) .
    for (int j = 1; j < num_buckets; j++) {
        if (random.next() < 1.0/(j+1)) b = j ;
    }
    return b;
}

即根据 key 生成 num_buckets-1 个(0~1.0)的伪随机数,依次检查随机数序列:

  • 第一个随机数 <1/2,则放第二个节点;
  • 第二个随机数 <1/3,则放第三个节点;
  • ...

注意伪装随机数序列由 key 唯一确定。意味着同样的内容在多次分配中,分配结果将是一致的。

另外,可以计算出如果当前分配为 b,下一个结果 j >= i 的概率(即 b+1 到 i-1 的过程都不发生跳变)

P(j>=i) = (b+1)/i

最终优化为如下代码:

int32_t JumpConsistentHash(uint64_t key, int32_t num_buckets) {
    int64_t b = -1, j = 0;
    while (j < num_buckets) {
        b = j;
        key = key * 2862933555777941757ULL + 1;
        j = (b + 1) * (double(1LL << 31) / double((key >> 33) + 1));
    }
    return b;
}

这个算法依赖于伪随机算法的输出,特点有: 分布均匀性由伪随机算法决定,而跟 key 自身均匀性无关,实践均匀性远好于基于环的算法; 不需要对 key 进行计算,复杂度低,实践计算时间也比基于环的算法好。

算法没解决的问题:当中间减掉节点的时候,序号会发生变化,变为 0...n-1。因此需要在外面维护一个映射关系,动态映射到原先的节点上。

转载请注明:http://blog.csdn.net/yeasy/article/details/50736182

项目实战:ActiveMQ开发大型分布式系统

消息中间件是搭建大型企业级项目必不可少的成员,只有掌握了消息中间件的使用和应用场景,才能开发真正的大型分布式系统。 ActiveMQ支持JMS,是apache的顶级项目,在全球开源消息中间件中影响力最大!
  • 2017年08月14日 15:46

分布式缓存的一致性Hash的Java实现

分布式缓存的一致性Hash的Java实现关于分布式缓存一致性Hash算法的原理,有很多书籍、博客都有详细的介绍。本文主要是想对一致性Hash算法进行一个小小的实现,方便自己更好的理解。算法的具体原理如...
  • u010412719
  • u010412719
  • 2016-12-24 22:27:48
  • 1964

分布式缓存--系列1 -- Hash环/一致性Hash原理

当前,Memcached、Redis这类分布式kv缓存已经非常普遍。从本篇开始,本系列将分析分布式缓存相关的原理、使用策略和最佳实践。我们知道Memcached的分布式其实是一种“伪分布式”,也就是它...
  • chunlongyu
  • chunlongyu
  • 2016-11-25 01:26:18
  • 2548

每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)

五分钟让你深入了解分布式系统中常用的一致性哈希算法
  • cywosp
  • cywosp
  • 2014-04-11 00:21:03
  • 368198

5行代码实现一致性哈希

你没看错,5行代码实现一致性哈希!好吧,我发现我真是越来越噱头了,原理请自行google,我自己分析了下,特分享出来,这个的一个缺点是加减机器只能从最右开始。 #include ...
  • gongyiling3468
  • gongyiling3468
  • 2015-08-20 10:42:57
  • 765

一致性Hash(Consistent Hashing)原理剖析

引入在业务开发中,我们常把数据持久化到数据库中。如果需要读取这些数据,除了直接从数据库中读取外,为了减轻数据库的访问压力以及提高访问速度,我们更多地引入缓存来对数据进行存取。读取数据的过程一般为: ...
  • lihao21
  • lihao21
  • 2017-01-08 00:07:28
  • 6460

一致性hash原理

一致性hash最常见的应用情形就是缓存,其实只要涉及到hash和多台主机请求路由的情况,都可能涉及到一致性hash问题。一致性hash主要是考虑到集群中节点挂掉或新增节点的时候,要对已有节点的影响降到...
  • luyaran
  • luyaran
  • 2016-12-15 09:34:19
  • 316

三分钟看懂一致性哈希算法

受一篇“五分钟看懂”的启发,来个哗众取宠的标题 一致性哈希算法,作为分布式计算的数据分配参考,比传统的取模,划段都好很多。 在电信计费中,可以作为多台消息接口机和在线计费主机的分配算法,根据ses...
  • gerryke
  • gerryke
  • 2016-12-30 09:25:21
  • 23226

《分布式》:paxos算法和一致性Hash算法相关记录

《分布式》:paxos算法和一致性Hash算法相关记录今天无意之间看到了这个关于分布式的协调一致的算法:paxos,于是找了一些相关博文看了下,说没有弄懂吧,感觉还懂点,说懂了吧,但是又感觉差点。哎,...
  • u010412719
  • u010412719
  • 2016-08-09 16:50:57
  • 908

一致性Hash算法介绍(分布式环境算法)

应用场景         1. 数据库分表分库规则,数据库服务器扩容降低对之前原有数据库数据的影响,并达到负载的均衡。         2. 分布式缓存负载算法规则,缓存数据库扩容降低对之前原有缓...
  • Jerome_s
  • Jerome_s
  • 2016-09-10 09:50:28
  • 3544
收藏助手
不良信息举报
您举报文章:分布式系统中的算法设计(一) -- 一致性 Hash
举报原因:
原因补充:

(最多只允许输入30个字)