一致性hash算法通常在分布式系统中运用比较普遍,例如多节点的数据存储,dubbo框架中也实现了一致性hash算法来选择服务提供者。还有在mysql集群,分库分表时,一致性hash也是比较经常使用的用来划分数据区域的算法。java
举个例子,电商公司的订单表,通常都会用数据库集群或者分库分表,用多个数据库实例在承载订单信息,这么大数据量的信息,在查询的时候,首先必需要保证幂等性,就是屡次调用hash算法得出的hash值都必须同样,这样才能保证在去查询订单信息的时候,都会到对应的数据库实例中去查询,不然就可能会查不到订单信息,以前经历的就是将订单表拆分为了512张表,按照订单时间来经行hash一致性算法node
一致性hash的数据结构:mysql
蓝色表示机器节点,橘黄色表示数据节点算法
真实节点:A B C Dsql
虚拟节点:A1 A2 B1 B2 C1 C2 D1 D2数据库
数据存放规则:计算出各个虚拟节点的hash值(不必定非要hash算法,能够是符合本身业务逻辑的算法,只要保证有效的数据经过hash算法可以在hash环内都有落点而且具备幂等性),例如:K1的hash值∈(A1,C2),则得出K1应该存放在C2节点数据结构
本章用java实现了两种hash一致性算法;框架
1.没有虚拟节点的hash环(逻辑环)分布式
2.带虚拟节点的hash环(逻辑环)测试
备注:我这里为了方便测试,直接给对应的真是节点的hash值写死了
实现一:
package com.dubbo.algorithm; import java.util.SortedMap; import java.util.TreeMap; public class ShardHash { //真实机器节点 <hash值,节点信息> private TreeMap<Long,String> realNodes = new TreeMap<Long,String>(); public ShardHash(){ init(); } public void init(){ // A B C D 4个真实节点 realNodes.put(1L, "A"); realNodes.put(6L, "B"); realNodes.put(14L, "C"); realNodes.put(25L, "D"); /* realNodes.put(hash("A"), "A"); realNodes.put(hash("B"), "B"); realNodes.put(hash("C"), "C"); realNodes.put(hash("D"), "D"); */ } public String getNode(String key){ //方便测试 Long keyHash = Long.valueOf(key); // Long keyHash = hash(key); //根据key的hash值,找到hash环上面比此hash值大的节点 SortedMap<Long,String> sorted = realNodes.tailMap(keyHash); if(sorted.isEmpty()){ //没有比key的hash大的节点,则该key存放在第一个节点上面 String node = realNodes.get(realNodes.firstKey()); return node; } String node = sorted.get(sorted.firstKey()); return node; } /** * 网上copy的一个hash算法,能够选择适合本身场景的hash算法 */ private Long hash(String str) { final int p = 16777619; Long hash = 2166136261L; for (int i = 0; i < str.length(); i++) hash = (hash ^ str.charAt(i)) * p; hash += hash << 13; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; // 若是算出来的值为负数则取其绝对值 if (hash < 0) hash = Math.abs(hash); return hash; } public static void main(String[] args) { ShardHash s = new ShardHash(); String node = s.getNode("33"); System.out.println("数据存放节点为:"+node); } }
实现二:
package com.dubbo.algorithm; import java.util.LinkedList; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; public class ShardHashV { //真实节点,4个真实节点 private List<String> realNodes = new LinkedList<String>(); //虚拟节点 private TreeMap<Long,String> vNode = new TreeMap<Long,String>(); //每一个真实节点对应多少个虚拟节点 private int VNODE_NUM = 4; //区间大小 private static Long count = 0L; public ShardHashV(){ init(); } //0--5--10--15.... public void init(){ realNodes.add("A1"); realNodes.add("B1"); realNodes.add("C1"); realNodes.add("D1"); for(int i=0;i<realNodes.size();i++){ for(int j=0;j<VNODE_NUM;j++){ vNode.put(hash(), "A"+(j+1)); vNode.put(hash(), "B"+(j+1)); vNode.put(hash(), "C"+(j+1)); vNode.put(hash(), "D"+(j+1)); } } } public String getNodes(String key){ //找到全部比key大的节点 SortedMap<Long,String> sm = vNode.tailMap(Long.valueOf(key)); if(sm.isEmpty()){ String node =vNode.get(vNode.firstKey()) ; return node; } String node =vNode.get(sm.firstKey()) ; return node; } public void remove(String key){ vNode.remove(Long.valueOf(key)); } public static void main(String[] args) { ShardHashV sv = new ShardHashV(); System.out.println(sv.getNodes("0")); System.out.println(sv.getNodes("7")); System.out.println(sv.getNodes("11")); System.out.println(sv.getNodes("16")); System.out.println(sv.getNodes("54")); //删除B1节点 sv.remove("10"); System.out.println(sv.getNodes("7")); sv.remove("5"); System.out.println(sv.getNodes("3")); } /** * 网上copy的一个hash算法,能够选择适合本身场景的hash算法 */ private Long hash() { Long key = this.count+5L; count=count+5L; return key; } }