Skip to content

xyz487/ConsistentHash

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

61 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

一致性哈希实现的缓存(OkCache)

license Percentage of issues still open Java CI with Maven GitHub followers GitHub forks GitHub stars GitHub watchers

整体架构

架构图 备注:所有数据分片存储到所以节点,当移除或增加1个node,仅影响差不多一个节点数据量的数据访问不到,其他90%的数据仍能一致性的获取到。

  • OkCache(Cache的Client端实现) 提供给用户透明一致的数据存取。
  • DistributedNodeManager(分布式节点管理器)
  1. 管理节点
  2. 提供key和节点的对应关系算法,实现分布式数据水平伸缩扩缩容的数据访问一致性。
  • Node(Cache的Server端实现) 真正的数据存储与获取。

测试输入:1000w个数据,100个服务器,每台服务器100个虚拟节点

注意:测试数据较大,需设置jvm参数(VM options: -Xms2g -Xmx2g

相同数据,算法不同:测试结果

  1. 采用CRC32的hash算法
开始存储10000000个kv数据
测试输出一个数据:
Name93939:0.07804853963130809
打印缓存统计信息如下:
服务器(192.168.0.0): 存储58508个数据
...
服务器(192.168.0.99): 存储93597个数据
总共100台服务器, kv总数据量:10000000个
平均值:100000.00
最大值:163129,(163.13%)
最小值:58508,(58.51%)
极差:104621,(104.62%)
标准差:17881.78,(17.88%)
  1. 采用Google Guava的哈希算法
开始存储10000000个kv数据
测试输出一个数据:
Name93939:0.6989422059059499
打印缓存统计信息如下:
服务器(192.168.0.0): 存储116470个数据
...
服务器(192.168.0.99): 存储104443个数据
总共100台服务器, kv总数据量:10000000个
平均值:100000.00
最大值:133019,(133.02%)
最小值:82196,(82.20%)
极差:50823,(50.82%)
标准差:7726.40,(7.73%)

数据的散列程度取决于hash算法的好坏。常见哈希算法:md5/sha1/sha128/sha256. java的hashcode的算法实现,散列度就差很多。但散列算法使用的不同场景,考虑的指标也不同:java的hashcode是快,分布式环境考虑数据的均匀分布,网络通信领域考虑数据的安全性

算法相同,数据和服务器数量不同:测试结果

  1. 10台服务器、100个虚拟节点
开始存储1000w个kv数据
...
总共10台服务器, kv总数据量:10000000个
平均值:1000000.00
最大值:1203222,(120.32%)
最小值:863464,(86.35%)
极差:339758,(33.98%)
标准差:78529.20,(7.85%)
  1. 10台服务器、1w个虚拟节点
开始存储1000w个kv数据
...
总共10台服务器, kv总数据量:10000000个
平均值:1000000.00
最大值:1019501,(101.95%)
最小值:976318,(97.63%)
极差:43183,(4.32%)
标准差:7902.40,(0.79%)

虚拟节点越大,标准差百分比越小,分布越均匀

结论:

一致性哈希算法的使用场景时分布式,重点考虑数据的散列程度和速度,不太关注安全性。它解决了数据分片时系统水平伸缩带来的数据失效问题。通过虚拟节点使每个节点均匀散列到环上,避免因数据倾斜导致系统负载不均衡问题。

附录:

一致性hash核心实现代码

    SortedMap<Integer, Node> hashCircle = new TreeMap<Integer, Node>();//java 排序树

    //初始化一致性hash环
    for(Node node : nodes) {
​       for (int i = 0; i < virtualNums; i++) {
​           hashCircle.put(hash(node.toString() + i), node);
        }
    }

    //计算key对应的服务器node
    int hash = getHash(key);  //计算key的hash值
    if (!hashCircle.containsKey(hash)) { //key的hash值是否和虚拟节点的hash相同
​       SortedMap<Integer, String> tailMap = hashCircle.tailMap(hash);//key的右子树
​       hash = tailMap.isEmpty() ? hashCircle.firstKey() : tailMap.firstKey();
​   }
    return hashCircle.get(hash);

一致性哈希:环搜索

一致性哈希:虚拟节点