当咱们在作数据库分库分表或者是分布式缓存时,不可避免的都会遇到一个问题:
程序员
如何将数据均匀的分散到各个节点中,而且尽可能的在加减节点时能使受影响的数据最少。算法
Hash 取模sql
随机放置就不说了,会带来不少问题。一般最容易想到的方案就是 hash 取模了。数据库
咱们能够将传入的 Key 按照 index = hash(key) % N 这样来计算出须要存放的节点。其中 hash 函数是一个将字符串转换为正整数的哈希映射方法,N 就是节点的数量。缓存
这样能够知足数据的均匀分配,可是这个算法的容错性和扩展性都较差。架构
好比增长或删除了一个节点时,全部的 Key 都须要从新计算,显然这样成本较高,为此须要一个算法知足分布均匀同时也要有良好的容错性和拓展性。并发
一致 Hash 算法分布式
一致 Hash 算法是将全部的哈希值构成了一个环,其范围在 0 ~ 2^32-1。函数
以下图高并发
以后将各个节点散列到这个环上,能够用节点的 IP、hostname 这样的惟一性字段做为 Key 进行 hash(key),散列以后以下:
以后须要将数据定位到对应的节点上,使用一样的 hash 函数 将 Key 也映射到这个环上。
这样按照顺时针方向就能够把 k1 定位到 N1节点,k2 定位到 N3节点,k3 定位到 N2节点。
容错性
这时假设 N1 宕机了:
依然根据顺时针方向,k2 和 k3 保持不变,只有 k1 被从新映射到了 N3。
这样就很好的保证了容错性,当一个节点宕机时只会影响到少少部分的数据。
拓展性
当新增一个节点时:
在 N2 和 N3 之间新增了一个节点 N4 ,这时会发现受印象的数据只有 k3,其他数据也是保持不变,因此这样也很好的保证了拓展性。
虚拟节点
到目前为止该算法依然也有点问题:当节点较少时会出现数据分布不均匀的状况:
这样会致使大部分数据都在 N1 节点,只有少许的数据在 N2 节点。
为了解决这个问题,一致哈希算法引入了虚拟节点。将每个节点都进行屡次 hash,生成多个节点放置在环上称为虚拟节点:
计算时能够在 IP 后加上编号来生成哈希值。这样只须要在原有的基础上多一步由虚拟节点映射到实际节点的步骤便可让少许节点也能知足均匀性。
欢迎工做一到五年的Java工程师朋友们加入Java程序员开发: 721575865
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用本身每一分每一秒的时间来学习提高本身,不要再用"没有时间“来掩饰本身思想上的懒惰!趁年轻,使劲拼,给将来的本身一个交代!