Skip to content

Instantly share code, notes, and snippets.

@arrayadd
Last active April 27, 2018 10:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arrayadd/12ff31dda58be5412b12b800e440c070 to your computer and use it in GitHub Desktop.
Save arrayadd/12ff31dda58be5412b12b800e440c070 to your computer and use it in GitHub Desktop.
一致性hash 一致在哪里?

关于一致性哈希算法的介绍,网上挺多的,图文并茂,易懂详细,例如:一致性哈希算法与Java实现


一些注意点

  • key的哈希及服务集群节点的哈希应该用同一个哈希函数。尽管理论上可以用不同函数,只需要保证这两个hash函数的哈希值范围及均匀性相近就行,但实际中完全没理由这么做,除非想没事找事,脱裤子放屁..
  • 每个节点的可虚拟节点数,及总的虚拟节点数,应该有已被实践过的参考范围,而不应该是越多越好。同时,随着实际节点数的变化,对应的总的虚拟节点数也应该是动态的。而不是线性的。
  • 实际开发中难点还有一处,就是如何有效的发现集群中节点的变化,也就是动态的删除,增加节点。

第三方工具类

redis,memcache的第三方Java Api,及guava 中都有一致性hash的工具类,可以直接使用,也不用纠结于合理的虚拟节点的数目该怎么取。例如Guava中,源码

 //guava源码说明
 public static int consistentHash(HashCode hashCode, int buckets) {
    return consistentHash(hashCode.padToLong(), buckets);
  }
  //入参 buckets 是实际的集群节点,例如 buckets=3,表示有3台机器 1,2,3  当然通过这三个数字,也要能找到对应的机器信息(IP,端口等)
  //入参 HashCode 这个类中有相应static函数,见下方,可以把实际查找的Key(无论是 int,long,String,byte[]) 转化为HashCode对象。
  //返回值:int, 就是经过一致性hash处理后的buckets 中的一个值。注意该值范围是 0~(buckets-1),类似数组下标从0开始一样。
  HashCode.fromBytes()
  HashCode.fromInt()
  HashCode.fromLong()
   HashCode.fromString()

一致性体现在哪里

k: 业务数据的key, n:集群机器数目

  • 传统方式:k%n, 当n变化,几乎绝大部分key(不是全部),都不会落在变化前所在机器节点上了。也就是对大部分key而言,增删节点,会导致所属节点变化,不再和原来保持一致了,如果是做缓存用,则会导致请求直接穿透到数据库,引起所谓雪崩。

Keys: 业务数据的key总数, n:集群机器数目

  • 一致性hash虚拟节点方式,目的是让keys可以随机均匀的分布到n中。
  • 当n增加的时候,原来的机器,会每个都拿出一部分key放到新增加机器中,以便让所有节点都占有大致相当的数量key. 这里注意,是每个节点都拿出一部分,给新增加机器,而不是像上面取模方式,几乎被打乱重新分配。
  • 当n减少时候,宕掉的节点,会把自己曾占有的key,均分的给还活着的机器。同样,这里也不是打乱重新分配。没挂掉的机器中所存储的key,并不会迁移变化,只是额外再分担些挂掉机器之前存储的那些key.

综上所述,一致性哈希Consistent Hashing 的所谓一致性,是对key而言的。非一致性哈希,取模方式,集群节点发生变化,key也跟着变化。而一致性哈希,集群节点发生变化,key很大情况下是不会跟着变化。相对而言是更靠近一致的。

有缺点吗?

一致性哈希算法,有缺点吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment