java一致性hash api_一致性哈希算法学习及JAVA代码实现分析
戳上面的蓝字关注我们哦!
本文作者:hapjin
欢迎点击下方阅读原文
1,对于待存储的海量数据,如何将它们分配到各个机器中去?---数据分片与路由
当数据量很大时,通过改善单机硬件资源的纵向扩充方式来存储数据变得越来越不适用,而通过增加机器数目来获得水平横向扩展的方式则越来越流行。因此,就有个问题,如何将这些海量的数据分配到各个机器中?数据分布到各个机器存储之后,又如何进行查找?这里主要记录一致性Hash算法如何将数据分配到各个机器中去。
2,衡量一致性哈希算法好处的四个标准:
①平衡性:平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。②单调性:单调性是指如果已经有一些数据通过哈希分配到了相应的机器上,又有新的机器加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的机器中去,而不会被映射到旧的机器集合中的其他机器上。
这里再解释一下:就是原有的数据要么还是呆在它所在的机器上不动,要么被迁移到新的机器上,而不会迁移到旧的其他机器上。
③分散性:④负载:参考这里:blog.csdn.net/cywosp/article/details/23397179
3,一致性哈希的原理:
由于一般的哈希函数返回一个int(32bit)型的hashCode。因此,可以将该哈希函数能够返回的hashCode表示成一个范围为0---(2^32)-1 环。
将机器的标识(如:IP地址)作为哈希函数的Key映射到环上。如:
hash(Node1) =Key1 hash(Node2) = Key2,借用一张图如下:
同样,数据也通过相同的哈希函数映射到环上。这样,按照顺时针方向,数据存放在它所在的顺时针方向上的那个机器上。这就是一致性哈希算法分配数据的方式!
4,JAVA实现一致性哈希算法的代码分析:
❶设计哈希函数
这里采用了MD5算法,主要是用来保证平衡性,即能够将机器均衡地映射到环上。貌似用Jdk中String类的hashCode并不能很好的保证平衡性。
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/*
* 实现一致性哈希算法中使用的哈希函数,使用MD5算法来保证一致性哈希的平衡性
*/
public class HashFunction{
private MessageDigest md5 = null;
public long hash(String key){
if (md5 == null) {
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("no md5 algrithm found");
}
}
md5.reset();
md5.update(key.getBytes());
byte[] bKey = md5.digest();
//具体的哈希函数实现细节--每个字节 & 0xFF 再移位
long result = ((long) (bKey[3] & 0xFF) << 24)
| ((long) (bKey[2] & 0xFF) << 16
| ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF));
return result & 0xffffffffL;
}
}
❷实现一致性哈希算法:
为什么要引入虚拟机器节点?它的作用是什么?
引入虚拟机器节点,其目的就是为了解决数据分配不均衡的问题。因为,在将实际的物理机器映射到环上时,有可能大部分机器都映射到环上的某一个部分(比如左半圆上),而通过引入虚拟机器节点,在进行机器hash映射时,不是映射具体机器,而是映射虚拟机器,并保证虚拟机器对应的物理机器是均衡的---每台实际的机器对应着相等数目的Virtual nodes。如下图:
如何解决集群中添加或者删除机器上需要迁移大量数据的问题?
假设采用传统的哈希取模法,设有K台物理机,H(key)=hash(key) mod K 即可实现数据分片。但当K变化时(如新增一台机器),所有已经映射好的数据都需要重新再映射。H(key)=hash(key) mod (K+1)。
一致性哈希采用的做法如下:引入一个环的概念,如上面的第一个图。先将机器映射到这个环上,再将数据也通过相同的哈希函数映射到这个环上,数据存储在它顺时针走向的那台机器上。以环为中介,实现了数据与机器数目之间的解藕。这样,当机器的数目变化时,只会影响到增加或删除的那台机器所在的环的邻接机器的数据存储,而其他机器上的数据不受影响。
在具体JAVA实现代码中,定义了一个TreeMap用来保存虚拟机器节点到实际的物理机器的映射。机器以字符串形式来标识,故hash函数的参数为String
for (int i = 0; i < numberOfReplicas; i++)
// 对于一个实际机器节点 node, 对应 numberOfReplicas 个虚拟节点
/*
* 不同的虚拟节点(i不同)有不同的hash值,但都对应同一个实际机器node
* 虚拟node一般是均衡分布在环上的,数据存储在顺时针方向的虚拟node上
*/
circle.put(hashFunction.hash(node.toString() + i), node);
而对于 数据的存储而言,逻辑上是按顺时针方向存储在虚拟机器节点中,虚拟机器节点通过TreeMap知道它实际需要将数据存储在哪台物理机器上。此外,TreeMap中的Key是有序的,而环也是顺时针有序的,这样才能当数据被映射到两台虚拟机器之间的弧上时,通过TreeMap的 tailMap()来寻找顺时针方向上的下一台虚拟机。
if (!circle.containsKey(hash)) {
//数据映射在两台虚拟机器所在环之间,就需要按顺时针方向寻找机器
SortedMap tailMap = circle.tailMap(hash);
hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
}
完整代码:
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
public class ConsistentHash{
private final HashFunction hashFunction;
private final int numberOfReplicas;
// 节点的复制因子,实际节点个数 * numberOfReplicas =虚拟节点个数
private final SortedMap circle = new TreeMap();
// 存储虚拟节点的hash值到真实节点的映射
public ConsistentHash(HashFunction hashFunction, int numberOfReplicas,
Collection nodes){
this.hashFunction = hashFunction;
this.numberOfReplicas = numberOfReplicas;
for (T node : nodes)
add(node);
}
public void add(T node){
for (int i = 0; i < numberOfReplicas; i++)
// 对于一个实际机器节点 node, 对应 numberOfReplicas 个虚拟节点
/*
* 不同的虚拟节点(i不同)有不同的hash值,但都对应同一个实际机器node
* 虚拟node一般是均衡分布在环上的,数据存储在顺时针方向的虚拟node上
*/
circle.put(hashFunction.hash(node.toString() + i), node);
}
public void remove(T node){
for (int i = 0; i < numberOfReplicas; i++)
circle.remove(hashFunction.hash(node.toString() + i));
}
/*
* 获得一个最近的顺时针节点,根据给定的key 取Hash
* 然后再取得顺时针方向上最近的一个虚拟节点对应的实际节点
* 再从实际节点中取得 数据
*/
public T get(Object key){
if (circle.isEmpty())
return null;
long hash = hashFunction.hash((String) key);
// node 用String来表示,获得node在哈希环中的hashCode
if (!circle.containsKey(hash)) {
//数据映射在两台虚拟机器所在环之间,就需要按顺时针方向寻找机器
SortedMap tailMap = circle.tailMap(hash);
hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
}
return circle.get(hash);
}
public long getSize(){
return circle.size();
}
/*
* 查看MD5算法生成的hashCode值---表示整个哈希环中各个虚拟节点位置
*/
public void testBalance(){
Set sets = circle.keySet();
//获得TreeMap中所有的Key
SortedSet sortedSets= new TreeSet(sets);
//将获得的Key集合排序
for(Long hashCode : sortedSets){
System.out.println(hashCode);
}
System.out.println("----each location 's distance are follows: ----");
/*
* 查看用MD5算法生成的long hashCode 相邻两个hashCode的差值
*/
Iterator it = sortedSets.iterator();
Iterator it2 = sortedSets.iterator();
if(it2.hasNext())
it2.next();
long keyPre, keyAfter;
while(it.hasNext() && it2.hasNext()){
keyPre = it.next();
keyAfter = it2.next();
System.out.println(keyAfter - keyPre);
}
}
public static void main(String[] args){
Set nodes = new HashSet();
nodes.add("A");
nodes.add("B");
nodes.add("C");
ConsistentHash consistentHash = new ConsistentHash(new HashFunction(), 2, nodes);
consistentHash.add("D");
System.out.println("hash circle size: " + consistentHash.getSize());
System.out.println("location of each node are follows: ");
consistentHash.testBalance();
}
}
参考:
blog.csdn.net/cywosp/article/details/23397179
blog.csdn.net/sparkliang/article/details/5279393
cnblogs.com/hupengcool/p/3659016.html
关注算法文摘,修炼开发内功
始发于微信公众号: 后端技术精选
java一致性hash api_一致性哈希算法学习及JAVA代码实现分析相关推荐
- 一致性哈希算法学习及JAVA代码实现分析
1,对于待存储的海量数据,如何将它们分配到各个机器中去?---数据分片与路由 当数据量很大时,通过改善单机硬件资源的纵向扩充方式来存储数据变得越来越不适用,而通过增加机器数目来获得水平横向扩展的方式则 ...
- 克鲁斯卡尔算法学习(Java)
克鲁斯卡尔算法学习(Java) 学习视频:尚硅谷韩老师java讲解数据结构和算法 一.应用场景-公交站问题 某城市新增 7 个站点(A, B, C, D, E, F, G) ,现在需要修路把 7 个站 ...
- KMP算法之NEXT数组代码原理分析 - 数据结构和算法38
KMP算法之NEXT数组代码原理分析 让编程改变世界 Change the world by program KMP算法之NEXT数组代码原理分析 NEXT数组:当模式匹配串T失配的时候,NEXT数组 ...
- 数据结构与算法之KMP算法中Next数组代码原理分析
2019独角兽企业重金招聘Python工程师标准>>> 一.KMP算法之Next数组代码原理分析 1.Next数组定义 当模式匹配串T失配的时候,Next数组对应的元素指 ...
- Java闭关修炼64课 很适合新手学习的JAVA视频教程
Java闭关修炼64课 很适合新手学习的JAVA视频教程 java闭关修炼第一课 什么是java(1).rar java闭关修炼第一课 什么是java.rar java闭关修炼第七课 基础语言 ...
- java 哈希算法_选择Java密码算法第1部分-哈希
java 哈希算法 抽象 这是涵盖Java加密算法的三部分博客系列文章的第1部分. 该系列涵盖如何实现以下功能: 使用SHA–512散列 使用AES–256的单密钥对称加密 使用RSA–4096的公钥 ...
- java和c 的rsa加密算法_RSA算法签名技术Java与C++统一(加密解密结果一样)
RSA算法签名技术Java与C++统一 (加密解密结果一样) 源代码下载地址:http://www.doczj.com/doc/64f44a94a0116c175f0e484d.html/produc ...
- Hash 浅谈哈希算法
哈希表 哈希表其实就是建立和存储一种映射关系 离散化.桶排序就是一种简单数值哈希 常见的哈希方法: 除法哈希法 hash(key) = keymod M(M为素数) 乘法哈希法 hash(key) ...
- Java 中(hash 0x7FFFFFFF)问题 哈希表中数组下标的计算
0x7FFFFFFF是一个用16进制表示的整型,是整型里面的最大值 转换成个二进制 0x7FFFFFFF 0111 1111 1111 1111 1111 1111 1111 1111(前31一个1代 ...
最新文章
- Keepalived运行命令
- Problem E: 零起点学算法25——判断是否直角三角形
- Copycat - 状态
- 两年摸爬滚打 Spring Boot,总结了这 16 条最佳实践
- vb对数据库操作用存储过程
- 抢了个票,还以为发现了12306的系统BUG
- QuickBI和DataV
- 遗传算法解决TSP问题
- 计算机专业介绍课件,计算机专业介绍课件
- 判断字符串是否为null、是否为空
- 概率论与数理统计 答案
- npm install没有node_文件,并且package.json文件缺失
- 蒋建平:国内云计算刚刚起步
- Vizard基础操作
- flask获取参数类型和请求响应
- 面试问题:如何开展接口测试
- 18天精读掌握《费曼物理学讲义卷一》 第5天 2019/6/18
- python大作业报告_python大作业含报告 相关实例(示例源码)下载 - 好例子网
- @JsonFormat将时间字符串2021-02-25T15:32:54+08:00解析成date
- [SQLite]浅析其一——SQLite数据库简介
热门文章
- rocketmq支持最大消息_分布式消息引擎Apache RocketMQ最佳实践
- 量子计算与量子信息_量子计算会破坏安全性吗?
- go语言编程项目_一个项目需要多少种编程语言?
- linksys 打印软件_Linksys对FCC表示“不”,Mozilla扩大了开源资金,还有更多新闻
- es6 为什么修饰器不能用于函数
- http://blog.csdn.net/rongdeguoqian/article/details/8035080
- php 返回object,深入分析使用mysql_fetch_object()以对象的形式返回查询结果
- job.php打不开,cronjob上的PHP错误,在提示时工作正常
- move语句java_java自动化代码优化
- kafka怎么监听oracle,Rango_lhl:Spark+Kafka实时监控Oracle数据预警