一致性哈希算法的Java实现

一致性哈希算法的Java实现

 

关于一致性哈希算法的原理,网上有很多介绍,在此只是简单介绍一下,不做详细说明。

 

一致性哈希算法是分布式系统中常用的算法,比如有N台缓存服务器,你需要将数据缓存到这N台服务器上。一致性哈希算法可以将数据尽可能平均的存储到N台缓存服务器上,提高系统的负载均衡,并且当有缓存服务器加入或退出集群时,尽可能少的影响现有缓存服务器的命中率,减少数据对后台服务的大量冲击。

 

一致性哈希算法的基本原理,把数据通过hash函数映射到一个很大的环形空间里,如下图所示:



 

 

 

A、B、C、D 4台缓存服务器通过hash函数映射环形空间上,数据的存储时,先得到一个hash值,对应到这个环中的每个位置,如缓存数据:K1对应到了图中所示的位置,然后沿顺时针找到一个机器节点A,将K1存储到A节点上,K2存储到A节点上,K3、K4存储到B节点上。

 

 

 

如果B节点宕机了,则B上的数据就会落到C节点上,如下图所示:



 

 

 

 

这样,只会影响C节点,对其他的节点A,D的数据不会造成影响。然而,这又会造成一个“雪崩”的情况,即C节点由于承担了B节点的数据,所以C节点的负载会变高,C节点很容易也宕机,这样依次下去,这样造成整个集群都挂了。

 

 

 

为此,引入了“虚拟节点”的概念:即把想象在这个环上有很多“虚拟节点”,数据的存储是沿着环的顺时针方向找一个虚拟节点,每个虚拟节点都会关联到一个真实节点,如下图所使用:

 



 

 

 

 

 

引入“虚拟节点”后,映射关系就从 {对象 ->节点 }转换到了 {对象 ->虚拟节点 }。图中的A1、A2、B1、B2、C1、C2、D1、D2都是虚拟节点,机器A负载存储A1、A2的数据,机器B负载存储B1、B2的数据,机器C负载存储C1、C2的数据。由于这些虚拟节点数量很多,均匀分布,提高了平衡性,因此不会造成“雪崩”现象。

 

 

 

说完了一致性哈希算法的基本原理,下面说一下一致性哈希算法的JAVA实现。

 

 

 

首先定义一个hash函数接口:HashFunction

 

/**
 * hash 函数接口
 * 
 * @author sundoctor
 * 
 */
public interface HashFunction {

	/**
	 * hash函数
	 * 
	 * @param key
	 * @return
	 */
	Long hash(String key);
}

 

 

HashFunction一个实现,定义HashFunction接口,为了方便大家根据业务需要实现自己的hash函数。

 

import java.nio.ByteBuffer;
import java.nio.ByteOrder;


public class HashFunctionImpl implements HashFunction {

	/**
	 * MurMurHash算法,是非加密HASH算法,性能很高,碰撞率低
	 */
	@Override
	public Long hash(String key) {
		ByteBuffer buf = ByteBuffer.wrap(key.getBytes());
		int seed = 0x1234ABCD;

		ByteOrder byteOrder = buf.order();
		buf.order(ByteOrder.LITTLE_ENDIAN);

		long m = 0xc6a4a7935bd1e995L;
		int r = 47;

		long h = seed ^ (buf.remaining() * m);

		long k;
		while (buf.remaining() >= 8) {
			k = buf.getLong();

			k *= m;
			k ^= k >>> r;
			k *= m;

			h ^= k;
			h *= m;
		}

		if (buf.remaining() > 0) {
			ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);			
			finish.put(buf).rewind();
			h ^= finish.getLong();
			h *= m;
		}

		h ^= h >>> r;
		h *= m;
		h ^= h >>> r;

		buf.order(byteOrder);
		return h;
	}

}

 

 

一致性哈希算法的JAVA简单实现:

 

 

import java.util.Collection;
import java.util.SortedMap;
import java.util.TreeMap;

public class ConsistentHash<T> {

	private final HashFunction hashFunction;// hash 函数接口
	private final int numberOfReplicas;// 每个机器节点关联的虚拟节点个数
	private final SortedMap<Long, T> circle = new TreeMap<Long, T>();// 环形虚拟节点

	/**
	 * 
	 * @param hashFunction
	 *            hash 函数接口
	 * @param numberOfReplicas
	 *            每个机器节点关联的虚拟节点个数
	 * @param nodes
	 *            真实机器节点
	 */
	public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<T> nodes) {
		this.hashFunction = hashFunction;
		this.numberOfReplicas = numberOfReplicas;

		for (T node : nodes) {
			add(node);
		}
	}

	/**
	 * 增加真实机器节点
	 * 
	 * @param node
	 */
	public void add(T node) {
		for (int i = 0; i < this.numberOfReplicas; i++) {
			circle.put(this.hashFunction.hash(node.toString() + i), node);
		}
	}

	/**
	 * 删除真实机器节点
	 * 
	 * @param node
	 */
	public void remove(T node) {
		for (int i = 0; i < this.numberOfReplicas; i++) {
			circle.remove(this.hashFunction.hash(node.toString() + i));
		}
	}

	/**
	 * 取得真实机器节点
	 * 
	 * @param key
	 * @return
	 */
	public T get(String key) {
		if (circle.isEmpty()) {
			return null;
		}

		long hash = hashFunction.hash(key);
		if (!circle.containsKey(hash)) {
			SortedMap<Long, T> tailMap = circle.tailMap(hash);// 沿环的顺时针找到一个虚拟节点
			hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
		}

		return circle.get(hash); // 返回该虚拟节点对应的真实机器节点的信息
	}
}

 

 

测试:

 

Node类模拟真实机器节点,保存节点的IP、名称、端口等

 

 

/**
 * 物理机节点模拟类,保存节点的IP、名称、端口等信息
 * 
 * @author sundoctor
 * 
 * @param <T>
 */
public class Node<T> {

	private String ip;// IP
	private String name;// 名称

	public Node(String ip, String name) {
		this.ip = ip;
		this.name = name;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 复写toString方法,使用节点IP当做hash的KEY
	 */
	@Override
	public String toString() {
		return ip;
	}

}

 

 

测试类:

 

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class Test {

	private static final String IP_PREFIX = "192.168.1.";// 机器节点IP前缀

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Map<String, Integer> map = new HashMap<String, Integer>();// 每台真实机器节点上保存的记录条数

		List<Node<String>> nodes = new ArrayList<Node<String>>();// 真实机器节点
		// 10台真实机器节点集群
		for (int i = 1; i <= 10; i++) {
			map.put(IP_PREFIX + i, 0);// 每台真实机器节点上保存的记录条数初始为0

			Node<String> node = new Node<String>(IP_PREFIX + i, "node" + i);
			nodes.add(node);
		}

		HashFunction hashFunction = new HashFunctionImpl(); // hash函数实例
		ConsistentHash<Node<String>> consistentHash = new ConsistentHash<Node<String>>(hashFunction, 100, nodes);// 每台真实机器引入100个虚拟节点

		// 将5000条记录尽可能均匀的存储到10台机器节点
		for (int i = 0; i < 5000; i++) {
			// 产生随机一个字符串当做一条记录,可以是其它更复杂的业务对象,比如随机字符串相当于对象的业务唯一标识
			String data = UUID.randomUUID().toString() + i;
			// 通过记录找到真实机器节点
			Node<String> node = consistentHash.get(data);
			// 再这里可以能过其它工具将记录存储真实机器节点上,比如MemoryCache等
			// ...
			// 每台真实机器节点上保存的记录条数加1
			map.put(node.getIp(), map.get(node.getIp()) + 1);
		}

		// 打印每台真实机器节点保存的记录条数
		for (int i = 1; i <= 10; i++) {
			System.out.println(IP_PREFIX + i + "节点记录条数:" + map.get("192.168.1." + i));
		}
	}
}

 

 

运行测试类Test,输出:

 

192.168.1.1节点记录条数:474

192.168.1.2节点记录条数:489

192.168.1.3节点记录条数:468

192.168.1.4节点记录条数:501

192.168.1.5节点记录条数:412

192.168.1.6节点记录条数:473

192.168.1.7节点记录条数:598

192.168.1.8节点记录条数:521

192.168.1.9节点记录条数:493

192.168.1.10节点记录条数:571

 

 

 

从以上输出可以看到,记录很均匀的分布在10机器节点。

 

 

 

一致性 Hash 算法 及Java TreeMap 实现 Redis Cluster 通过分片的方式将整个缓存划分为 16384 个槽,每个缓存节点就相当于 Hash 换上的一个节点,接入集群时,所有实例都将均匀占有这些哈希槽,当需要查询一个 Key 是,首先根据 Key 的 hashcode 对 16384 取余来得到 Key 属于哪个槽,并映射到缓存实例上。当缓存服务器需要扩容的时候,只需要将新加入缓存服务器的 Hash 值放入一致性 Hash 环中,由于 Key 是顺时针查找距离其最近的节点,因此新加入的几点只影响整个环中的一小段。 阅读详情

相关推荐

【无机纳米材料科研制图——OriginLab 0205】Origin对BET吸附/脱附曲线及孔径分布制图...

此篇,我们来分享如何对BET数据进行处理和制图。 一、等温吸脱附曲线绘制 1)找到等温吸脱附文件。 一半来说,对一个样品测一次BET,会产生很多数据,具体因仪器而异。我这里的数据主要有两个文件夹,一个脱附,一个吸附,吸附文件夹中多了一个等温吸脱附的文件夹。而这...

XD_Yangf的博客 803

一致性hash算法及java实现

一致性hash算法是分布式中一个常用且好用的分片算法、或者数据库分库分表算法。现在的互联网服务架构中,为避免单点故障、提升处理效率、横向扩展等原因,分布式系统已经成为了居家旅行必备的部署模式,所以也产出了几种数据分片的方法: 1.取模,2.划段,3.一致性hash 前两种有很大的一个问题就是需要固定的节点数,即节点数不能变,不能某一个节点挂了或者实时增加一个节点,变了分片规则就需要改变,需要迁...

青鱼入云的博客 1万+

【Unity3D渲染】Unity3d实现Photoshop的各种图层混合模式

Adobe Photoshop图层混合原理、Unity ShaderLab图层混合实现

a310989583的专栏 2435

一致性hash算法Java实现

一致性hash算法Java实现

weixin_38387358的博客 904

使用Qt Creator调用eBUS SDK

在新界面,勾选左下角“ Show unreachable Network Devices",可以看到没有成功连接的网络相机,原因是IP地址配置错误。每个版本的OpenCV的依赖项的名字都不同,我这里是opencv_world490.lib与opencv_world490d.lib。函数,可以看到在这里是图像缓存区,能够对捕获的图像进行一些操作。在Visual Studio打开项目属性,在VC++目录这里,把Opencv的路径添加进去。这时,会自动跳转到mainwindow.cpp,生成该按钮对应的槽函数。

qq_35685051的博客 1288

Zabbix故障排查10051端口无法启动:zabbix-server.service: main process exited, code=exited, status=1/FAILURE

zabbix-server.service: main process exited, code=exited, status=1/FAILURE报错说与数据库失去连接。

" DevOps菜鸟(Xu JieHao)" 的博客 2255

JAVA实现一致性Hash算法

介绍 一致性Hash算法是实现负载均衡的一种策略,后续会写如何实现负载均衡 一致哈希是一种特殊的哈希算法。 在使用一致哈希算法后,哈希表槽位数(大小)的改变平均只需要对 K/n个关键字重新映射,其中K是关键字的数量, n是槽位数量。 然而在传统的哈希表中,添加或删除一个槽位的几乎需要对所有关键字进行重新映射。 强哈希 考虑到单服务器不能承载,因此使用了分布式架构,最初的算法为 hash() mod n, hash()通常取用户ID,n为节点数。 此方法容易实现且能够满足运营要求。缺点是当单点发

qq_49173222的博客 3036

Unity IAP插件实战:从零搭建Google Play内购系统

本文详细介绍了如何使用Unity IAP插件从零搭建Google Play内购系统,涵盖环境准备、双端配置、核心API实现及常见问题排查。通过实战代码示例,帮助开发者快速掌握跨平台内购功能开发,提升手游商业化效率。

weixin_28692867的博客 430

一致性哈希算法与Java实现

一致性哈希算法是分布式系统中常用的算法。比如,一个分布式的存储系统,要将数据存储到具体的节点上, 如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数, 如果有一个机器加入或退出这个集群,则所有的数据映射都无效了。

小僧下山踏红尘 178

05、基于ALV表报实现复选框、全选、取消全选等功能

基于Function ALV实现复选框、全选及取消全选等功能

wo520yl1314的博客 2372

一款值得关注的开源企业管理ERP系统

盒木ERP是一款开源的现代化企业管理系统,采用前后端分离架构,后端基于Python+Django,前端使用Vue+Ant Design。系统涵盖采购、销售、库存、财务等核心业务模块,支持移动端操作和PDA扫码。具有开源免费、技术先进、功能完善等特点,适合中小企业使用或二次开发。项目提供详细部署指南,支持Docker容器化部署,是学习ERP系统开发和企业数字化转型的优秀选择。

1107

一致性hash算法的java实现_Java_一致性哈希算法与Java实现

摘自:http://blog.youkuaiyun.com/wuhuan_wp/article/details/7010071一致性哈希算法是分布式系统中常用的算法。比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数...

weixin_42549723的博客 266

【GEOS-Chem模拟教程第二期下】全球生物质燃烧 GFAS 数据配置

本博客详细介绍全球生物质燃烧 GFAS 数据下载及在 GEOS-CHEM 模型中的配置

WW、forever的博客 1395

一致性哈希 java实现_一致性哈希算法介绍,及java实现

https://www.cnblogs.com/hupengcool/p/3659016.html应用场景在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin)、哈希算法(HASH)、最少连接算法(Least Connection)、响应速度算法(Response Time)、加权法(Weighted )等。其中哈希算法是最为常用的算法.典型的应用场景是:...

weixin_30016593的博客 304

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

java murmurhash实现_一致性哈希算法与Java实现

一致性哈希算法是分布式系统中常用的算法。比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了。因此,引入了一致性哈希算法:把数据用hash函数(如MD5),映射到...

weixin_32394015的博客 1136

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

一致性哈希算法学习及JAVA代码实现分析

1,对于待存储的海量数据,如何将它们分配到各个机器中去?---数据分片与路由 当数据量很大时,通过改善单机硬件资源的纵向扩充方式来存储数据变得越来越不适用,而通过增加机器数目来获得水平横向扩展的方式则越来越流行。因此,就有个问题,如何将这些海量的数据分配到各个机器中?数据分布到各个机器存储之后,又如何进行查找?这里主要记录一致性Hash算法如何将数据分配到各个机器中去。 2,衡量一致性哈...

z69183787的专栏 820

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

一致性哈希算法 mysql_一致性哈希算法(Java实现)

前言一致性哈希算法在分布式系统的应用中是十分广泛的。常见的应用场景是分布式缓存。它主要解决了哈希取模算法在分布式系统中存在的动态伸缩等问题。哈希取模算法的局限性在分布式缓存集群中,当新增加缓存服务器或其中一台挂掉后,由路由算法发生改变,导致大量的缓存数据不能命中。从而造成数据库面临巨大压力而崩溃,可能导致整个系统崩溃。一致性哈希算法原理一致性哈希算法通过一个叫作一致性哈希环的数据结构实现。这个环的...

weixin_39553352的博客 353
上一篇: jquery 图片上传前预览,支持ie6+、firefox、chrome
下一篇: 扩展spring boot cache实现redis一二级分布式缓存
sundoctor
博客等级 码龄23年 2粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值