前言

在前文分布式理论(八)—— Consistent Hash(一致性哈希算法)中,我们讨论了一致性 hash 算法的原理,并说了,我们会自己写一个简单的算法。今天就来写一个。

普通 hash 的结果

先看看普通 hash 怎么做。

首先,需要缓存节点对象,缓存中的存储对象,还有一个缓存节点集合,用于保存有效的缓存节点。

  1. 实际存储对象,很简单的一个类,只需要获取他的 hash 值就好:
  static class Obj {
String key;
Obj(String key) {
this.key = key;
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return "Obj{" +
"key='" + key + '\'' +
'}';
}
}
  1. 缓存节点对象,用于存储实际对象:
  static class Node {

    Map<Integer, Obj> node = new HashMap<>();
String name; Node(String name) {
this.name = name;
} public void putObj(Obj obj) {
node.put(obj.hashCode(), obj);
} Obj getObj(Obj obj) {
return node.get(obj.hashCode());
} @Override
public int hashCode() {
return name.hashCode();
}
}

也很简单,内部使用了一个 map 保存节点。

  1. 缓存节点集合,用于保存有效的缓存节点:
 static class NodeArray {

    Node[] nodes = new Node[1024];
int size = 0; public void addNode(Node node) {
nodes[size++] = node;
} Obj get(Obj obj) {
int index = obj.hashCode() % size;
return nodes[index].getObj(obj);
} void put(Obj obj) {
int index = obj.hashCode() % size;
nodes[index].putObj(obj);
}
}

内部一个数组,取数据时,通过取余机器数量获取缓存节点,再从节点中取出数据。

  1. 测试:当增减节点时,还能不能找到原有数据:
 /**
* 验证普通 hash 对于增减节点,原有会不会出现移动。
*/
public static void main(String[] args) { NodeArray nodeArray = new NodeArray(); Node[] nodes = {
new Node("Node--> 1"),
new Node("Node--> 2"),
new Node("Node--> 3")
}; for (Node node : nodes) {
nodeArray.addNode(node);
} Obj[] objs = {
new Obj("1"),
new Obj("2"),
new Obj("3"),
new Obj("4"),
new Obj("5")
}; for (Obj obj : objs) {
nodeArray.put(obj);
} validate(nodeArray, objs);
}
  private static void validate(NodeArray nodeArray, Obj[] objs) {
for (Obj obj : objs) {
System.out.println(nodeArray.get(obj));
} nodeArray.addNode(new Node("anything1"));
nodeArray.addNode(new Node("anything2")); System.out.println("========== after ============="); for (Obj obj : objs) {
System.out.println(nodeArray.get(obj));
}
}

测试步骤如下:

  1. 向集合中添加 3 个节点。
  2. 集群 中添加 5 个对象,这 5 个对象会根据 hash 值散列到不同的节点中。
  3. 打印 未增减前 的数据。
  4. 打印 增加 2 个节点 后数据,看看还能不能访问到数据。

结果:

一个都访问不到了。这就是普通的取余的缺点,在增减机器的情况下,这种结果无法接收。

再看看一致性 hash 如何解决。

一致性 Hash 的结果

关键的地方来了。

缓存节点对象和实际保存对象不用更改,改的是什么?

改的是保存对象的方式和取出对象的方式,也就是不使用对机器进行取余的算法。

新的 NodeArray 对象如下:

static class NodeArray {

/** 按照 键 排序*/
TreeMap<Integer, Node> nodes = new TreeMap<>(); void addNode(Node node) {
nodes.put(node.hashCode(), node);
} void put(Obj obj) {
int objHashcode = obj.hashCode();
Node node = nodes.get(objHashcode);
if (node != null) {
node.putObj(obj);
return;
} // 找到比给定 key 大的集合
SortedMap<Integer, Node> tailMap = nodes.tailMap(objHashcode);
// 找到最小的节点
int nodeHashcode = tailMap.isEmpty() ? nodes.firstKey() : tailMap.firstKey();
nodes.get(nodeHashcode).putObj(obj); } Obj get(Obj obj) {
Node node = nodes.get(obj.hashCode());
if (node != null) {
return node.getObj(obj);
} // 找到比给定 key 大的集合
SortedMap<Integer, Node> tailMap = nodes.tailMap(obj.hashCode());
// 找到最小的节点
int nodeHashcode = tailMap.isEmpty() ? nodes.firstKey() : tailMap.firstKey();
return nodes.get(nodeHashcode).getObj(obj);
}
}

该类和之前的类的不同之处在于:

  1. 内部没有使用数组,而是使用了有序 Map。
  2. put 方法中,对象如果没有落到缓存节点上,就找比他小的节点且离他最近的。这里我们使用了 TreeMap 的 tailMap 方法,具体 API 可以看文档。
  3. get 方法中,和 put 步骤相同,否则是取不到对象的。

具体寻找节点的方式如图:

相同的测试用例,执行结果如下:

找到了之前所有的节点。解决了普通 hash 的问题。

总结

代码比较简单,主要是通过 JDK 自带的 TreeMap 实现的寻找临近节点。当然,我们这里也只是测试了添加,关于修改还没有测试,但思路是一样的。这里只是做一个抛砖引玉。

同时,我们也没有实现虚拟节点,感兴趣的朋友可以尝试一下。

good luck!!!!

自己实现一个一致性 Hash 算法的更多相关文章

  1. 对一致性Hash算法,Java代码实现的深入研究

    一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...

  2. 一致性hash算法详解

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179     一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...

  3. 一致性hash算法简介

    一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希 ...

  4. 分布式缓存技术memcached学习(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...

  5. 一致性 hash 算法( consistent hashing )a

    一致性 hash 算法( consistent hashing ) 张亮 consistent hashing 算法早在 1997 年就在论文 Consistent hashing and rando ...

  6. 一致性hash算法简介与代码实现

    一.简介: 一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义: 1.平衡性(Balance) 2.单调性(Monotonicity) 3.分散性(Spread) 4.负 ...

  7. memcache的一致性hash算法使用

    一.概述 1.我们的memcache客户端(这里我看的spymemcache的源码),使用了一致性hash算法ketama进行数据存储节点的选择.与常规的hash算法思路不同,只是对我们要存储数据的k ...

  8. 一致性Hash算法在Redis分布式中的使用

    由于redis是单点,但是项目中不可避免的会使用多台Redis缓存服务器,那么怎么把缓存的Key均匀的映射到多台Redis服务器上,且随着缓存服务器的增加或减少时做到最小化的减少缓存Key的命中率呢? ...

  9. 【转载】一致性hash算法释义

    http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Karge ...

随机推荐

  1. apollo broker 启动流程

    1.下载Apollo服务器,下载后解压,然后运行apache-apollo-1.6\bin\apollo.cmd,输入create mybroker(名字任意取,这里是根据官网介绍的来取的)创建服务器 ...

  2. C# Winform右下角弹窗方式

    [方法一] 第一步:winform项目创建完成后,添加一个窗口,命名为:Messages .(加上最开始的Form1,一共为两个窗口),双击主窗口进入后台代码 . 第二步:在Messages 窗口中添 ...

  3. WPF 右上角带数字的按钮

    效果如图所示 三种方案, 1:不改控件模版,布局实现,死开 2:改button模版,利用附加属性,附加附加属性,功能多了话,不利于拓展 3:继承button,添加依赖属性,接下来是这种 1:新建类 为 ...

  4. VS2017+EF+Mysql生成实体数据模型(解决闪退的坑) 版本对应才行

    最近要使用VS2017+EF+Mysql,在生成实体数据模型踏过一些坑,在此做个总结. 1.先下载并安装 mysql-connector-net-6.9.10.msi  和  mysql-for-vi ...

  5. Asp.Net MVC EF之二:原生EF插入,更新数据的正确方法

    引言 EF是相对与Dapper.NHibernate官方首推的ORM框架,其在开发过程中的方便,快捷毋庸置疑的,但由于EF本身的一些缓存机制.跟踪机制,所以在使用时有些地方需要特别注意. 下面我将自己 ...

  6. 【有新题】OCP 12c 062出现大量新考题-14

    choose two You plan to upgrade your Oracle Database 9i to Oracle Database 12c. Which two methods can ...

  7. “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. Java Calender 类详解

    一.   如何创建 Calendar 对象 Calendar 是一个抽象类, 无法通过直接实例化得到对象. 因此, Calendar 提供了一个方法 getInstance,来获得一个Calendar ...

  9. Python 一篇学会多线程

    多线程和多进程是什么自行google补脑,廖雪峰官网也有,但是不够简洁,有点晕,所以就整个简单的范例. 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂.所以,这里力图用 ...

  10. D01-R语言基础学习

    R语言基础学习——D01 20190410内容纲要: 1.R的下载与安装 2.R包的安装与使用方法 (1)查看已安装的包 (2)查看是否安装过包 (3)安装包 (4)更新包 3.结果的重用 4.R处理 ...