导航:首页 > 源码编译 > 一致性哈希算法java

一致性哈希算法java

发布时间:2023-06-15 11:13:38

java去公司工作用到的是哪方面的知识

小公司做小项目一般都用SSH+jsp大公司做项目都是根据不同的项目 采取不同的框架技术,比如银行大部分都用 EJB等

第一:先学习Java的核心库(JavaSE)

JavaSE的内容包括:环境搭建、基础语法、面向对象、数组、集合、常用类、IO流、反射机制、网络编程……..

第二:MySQL数据库

搞定一门数据库相关的课程,例如:MySQL、Oracle,搞定一个就可以了,目前互联网公司,例如:京东、阿里等,他们都在使用MySQL,所以建议大家学习MySQL数据库,小巧轻盈,免费,由于互联网公司的项目访问量比较大,所以一般会搭建数据库的集群,可以一个数据库不够,所以需要搭建数据库集群,为了应付高并发。(搭建的比较多的时候,免费就很重要了。)

第三:WEB前端

以后从事Java开发,从事JavaEE开发,主要开发的系统结构是B/S结构的,B指的是Browser,S指的是Server。要开发这种系统,B端要会,S端也要精通。WEB前端的学习就是学习B端技术。包括:HTML 、CSS、JavaScript(JS)、jQuery框架(底层对JS进行了封装)…

第四:WEB后端(JavaWEB)

WEB后端其实可以是很多种不同的编程语言,例如:PHP、C、C++、Java,他们都可以进行WEB后端的开发,我们既然选择了比较火爆的Java,那么我们学习的后端一定是基于Java语言实现的,包括:Servlet、Filter、Jsp、EL、JSTL、MVC架构模式、数据库连接池(阿里巴巴的Druid连接池)、代理模式(动态代理)。另外后端学习了之后,还要学习一个异步编程技术AJAX。(完成网页的局部刷新,AJAX其实不属于后端,是前端浏览器上的程序。)

学习到这里为止,表示Java基本/基础的技术已经学完了。但是这些最基层的技术在实际的开发中不会使用的,一般为了开发效率,都会使用大量的提前封装好的框架。

第五:最好能够停留下来,做一个项目。

这个项目最好能将之前所学全部串起来。(对以前的知识点进行巩固。)

这个项目最好是基于:Servlet + Jsp+AJAX+jQuery+MySQL….

在这个项目的开发过程中:大家一定要记住,目前比较好的项目自动构建工具:Maven是一定要精通的。还有一个就是团队协作开发:Git/SVN是一定要会用的。(目前使用Git比较多一些。)

第六:学习高级框架

Spring、SpringMVC、MyBatis(持久层框架,这个框架互联网公司使用比较多,因为互联网项目需要进行SQL优化,MyBatis的SQL优化很方便,所以大部分都是使用MyBatis)

Struts2(很少使用了,使用这个的肯定是很老的项目)、Hibernate(传统企业,还有政府等可能会使用Hibernate。)

SpringBoot(新项目大部分使用的都是boot了。所以在项目中遇到还在使用SSM的一般都是遗留项目。)

当你走到这里之后,基本上你可以出山了。(去找工作,8K的薪资应该问题不大,但前提是你学的好。学习的深度够了,广度够了。)

第七:最好能有一个大型项目是使用框架来完成的。

SpringBoot做一个项目。

Spring SpringMVC MyBatis做一个项目。

这个项目最好是找几个人搭伙做一下。体验一下团队协作。(尤其是使用一些协作的工具。怎么沟通,怎么写日报,怎么开会,怎么使用Git,等等….)

第八:如果你的薪资想达到15K的话,你可能需要还要学习一些分布式相关的一些技术。

能够应付高并发的一些技术,例如:分布式框架Dubbo、SpringCloud、MQ、Nginx、Redis…..

java的知识体系构架

....祝 工作顺心 哈哈

⑵ 如何正确实现Java中的hashCode方法

正确实现Java中的hashCode方法:
相等和哈希码

相等是从一般的方面来讲,哈希码更加具有技术性。如果我们在理解方面存在困难,我们可以说,他们通过只是一个实现细节来提高了性能。
大多数的数据结构通过equals方法来判断他们是否包含一个元素,例如:
List<String> list = Arrays.asList("a", "b", "c");
boolean contains = list.contains("b");

这个变量contains结果是true,因为,虽然”b”是不相同的实例(此外,忽略字符串驻留),但是他们是相等的。
通过比较实例的每个元素,然后将比较结果赋值给contains是比较浪费的,虽然整个类的数据结构进行了优化,能够提升性能。
他们通过使用一种快捷的方式(减少潜在的实例相等)进行比较,从而代替通过比较实例所包含的每个元素。而快捷比较仅需要比较下面这些方面:
快捷方式比较即通过比较哈希值,它可以将一个实例用一个整数值来代替。哈希码相同的实例不一定相等,但相等的实例一定具有有相同的哈希值。(或应该有,我们很快就会讨论这个)这些数据结构经常通过这种这种技术来命名,可以通过Hash来识别他们的,其中,HashMap是其中最着名的代表。
它们通常是这样这样运作的
当添加一个元素,它的哈希码是用来计算内部数组的索引(即所谓的桶)
如果是,不相等的元素有相同的哈希码,他们最终在同一个桶上并且捆绑在一起,例如通过添加到列表。
当一个实例来进行contains操作时,它的哈希码将用来计算桶值(索引值),只有当对应索引值上存在元素时,才会对实例进行比较。
因此equals,hashCode是定义在Object类中。
散列法的思想
如果hashCode作为快捷方式来确定相等,那么只有一件事我们应该关心:相等的对象应该具有相同的哈希码,这也是为什么如果我们重写了equals方法后,我们必须创建一个与之匹配的hashCode实现的原因!
否则相等的对象是可能不会有相同的哈希码的,因为它们将调用的是Object's的默认实现。
HashCode 准则
引用自官方文档
hashCode通用约定:
* 调用运行Java应用程序中的同一对象,hashCode方法必须始终返回相同的整数。这个整数不需要在不同的Java应用程序中保持一致。
* 根据equals(Object)的方法来比较,如果两个对象是相等的,两个对象调用hashCode方法必须产生相同的结果。
* 根据equals(Object)的方法是比较,如果两个对象是不相等的,那么两个对象调用hashCode方法并不一定产生不同的整数的结果。但是,程序员应该意识到给不相等的对象产生不同的整数结果将有可能提高哈希表的性能。
第一点反映出了相等的一致性属性,第二个就是我们上面提出的要求。第三个阐述了一个重要的细节,我们将在稍后讨论。
HashCode实现
下面是非常简单的Person.hashCode的实现
@Override
public int hashCode() {
return Objects.hash(firstName, lastName);
}

person’s是通过多个字段结合来计算哈希码的。都是通过Object的hash函数来计算。
选择字段
但哪些字段是相关的吗?需求将会帮助我们回答这个问题:如果相等的对象必须具有相同的哈希码,那么计算哈希码就不应包括任何不用于相等检查的字段。(否则两个对象只是这些字段不同但是仍然有可能会相等,此时他们这两个对象哈希码却会不相同。)
所以用于哈希组字段应该相等时使用的字段的子集。默认情况下都使用相同的字段,但有一些细节需要考虑。
一致性
首先,有一致性的要求。它应该相当严格。虽然它允许如果一些字段改变对应的哈希码发生变化(对于可变的类是不可避免的),但是哈希数据结构并不是为这种场景准备的。
正如我们以上所见的哈希码用于确定元素的桶。但如果hash-relevant字段发生了改变,并不会重新计算哈希码、也不会更新内部数组。
这意味着以后通过相等的对象,甚至同一实例进行查询也会失败,数据结构计算当前的哈希码与之前存储实例计算的哈希码并不一致,并是错误的桶。
结论:最好不要使用可变字段计算哈希码!
性能
哈希码最终计算的频率与可能调用equals差不多,那么这里将是影响性能的关键部分,因此考虑此部分性能也是非常有意义的。并且与equals相比,优化之后又更大的上升空间。
除非使用非常复杂的算法或者涉及非常多的字段,那么计算哈希码的运算成本是微不足道的、同样也是不可避免的。但是也应该考虑是否需要包含所有的字段来进行运算。集合需要特别警惕的对待。以Lists和sets为例,将会包含集合里面的每一个元素来计算哈希码。是否需要调用它们需要具体情况具体分析。
如果性能是至关重要的,使用Objects.hash因为需要为varargs创建一个数组也许并不是最好的选择。但一般规则优化是适用的:不要过早地使用一个通用的散列码算法,也许需要放弃集合,只有优化分析显示潜在的改进。
碰撞
总是关注性能,这个实现怎么呢?
@Override
public int hashCode() {
return 0;
}

快是肯定的。相等的对象将具有相同的哈希码。并且,没有可变的字段!
但是,我们之前说过的桶呢?!这种方式下所有的实例将会有相同的桶!这将会导致一个链表来包含所有的元素,这样一来将会有非常差的性能。每次调用contains将会触发对整个list线性扫描。
我们希望尽可能少的元素在同一个桶!一个算法返回变化多端的哈希码,即使对于非常相似的对象,是一个好的开始。
怎样才能达到上面的效果部分取决于选取的字段,我们在计算中包含更多的细节,越有可能获取到不同的哈希码。注意:这个与我们所说的性能是完全相反的。因此,有趣的是,使用过多或者过少的字段都会导致糟糕的性能。
防止碰撞的另一部分是使用实际计算散列的算法。
计算Hsah
最简单的方法来计算一个字段的哈希码是通过直接调用hashCode,结合的话会自动完成。常见的算法是首先在以任意数量的数值(通常是基本数据类型)反复进行相乘操作再与字段哈希码相加
int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
return result;

这可能导致溢出,但是不是特别有问题的,因为他们并没有产生Java异常。
注意,即使是非常良好的的哈希算法也可能因为输入特定的模式的数据有导致频繁碰撞。作为一个简单的例子假设我们会计算点的散列通过增加他们的x和y坐标。当我们处理f(x) = -x线上的点时,线上的点都满足:x + y == 0,将会有大量的碰撞。
但是:我们可以使用一个通用的算法,只到分析表明并不正确,才需要对哈希算法进行修改。
总结
我们了解到计算哈希码就是压缩相等的一个整数值:相等的对象必须有相同的哈希码,而出于对性能的考虑:最好是尽可能少的不相等的对象共享相同的哈希码。
这就意味着如果重写了equals方法,那么就必须重写hashCode方法
当实现hashCode
使用与equals中使用的相同的字段(或者equals中使用字段的子集)
最好不要包含可变的字段。
对集合不要考虑调用hashCode
如果没有特殊的输入特定的模式,尽量采用通用的哈希算法
记住hashCode性能,所以除非分析表明必要性,否则不要浪费太多的精力。

⑶ 一致性hash算法是什么

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

一致性Hash是一种特殊的Hash算法,由于其均衡性、持久性的映射特点,被广泛的应用于负载均衡领域,如nginx和memcached都采用了一致性Hash来作为集群负载均衡的方案。

一致性哈希算法的目标是,当K个请求key发起请求时。后台增减节点,只会引起K/N的key发生重新映射。即一致性哈希算法,在后台节点稳定时,同一key的每次请求映射到的节点是一样的。而当后台节点增减时,该算法尽量将K个key映射到与之前相同的节点上。

构成哈希算法的条件:

从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法)。

对输入数据非常敏感,哪怕原始数据只修改了一个 Bit,最后得到的哈希值也大不相同。

散列冲突的概率要很小,对于不同的原始数据,哈希值相同的概率非常小。

哈希算法的执行效率要尽量高效,针对较长的文本,也能快速地计算出哈希值。

⑷ 如何用java获取redis的info

预备
jedis-2.5.2
commons-pool2-2.2.jar
使用单连接
此方式仅建议用于开发环境做调试用。
// 创建连接
String host = "192.168.56.102";
int port = 6379;
Jedis client = new Jedis(host, port);
// 执行set指令
String result = client.set("key-string", "Hello, Redis!");
System.out.println( String.format("set指令执行结果:%s", result) );
// 执行get指令
String value = client.get("key-string");
System.out.println( String.format("get指令执行结果:%s", value) );
运行上述代码,控制台输出:
set指令执行结果:OK
get指令执行结果:Hello, Redis!
使用连接池
此方式适用于仅使用单个Redis实例的场景。
// 生成连接池配置信息
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(10);
config.setMaxTotal(30);
config.setMaxWaitMillis(3*1000);

// 在应用初始化的时候生成连接池
JedisPool pool = new JedisPool(config, "192.168.56.102", 6379);

// 在业务操作时,从连接池获取连接
Jedis client = pool.getResource();
try {
// 执行指令
String result = client.set("key-string", "Hello, Redis!");
System.out.println( String.format("set指令执行结果:%s", result) );
String value = client.get("key-string");
System.out.println( String.format("get指令执行结果:%s", value) );
} catch (Exception e) {
// TODO: handle exception
} finally {
// 业务操作完成,将连接返回给连接池
if (null != client) {
pool.returnResource(client);
}
} // end of try block

// 应用关闭时,释放连接池资源
pool.destroy();

运行上述代码,控制台输出:

set指令执行结果:OK
get指令执行结果:Hello, Redis!
使用连接池+分布式
在规模较大的系统中,往往会有多个Redis实例做负载均衡。并且还实现主从备份,当主实例发生故障时,切换至从实例提供服务。
类似于Memcached的客户端,Jedis也提供了客户端分布式操作的方式,采用一致性哈希算法。
// 生成多机连接信息列表
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
shards.add( new JedisShardInfo("127.0.0.1", 6379) );
shards.add( new JedisShardInfo("192.168.56.102", 6379) );

// 生成连接池配置信息
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(10);
config.setMaxTotal(30);
config.setMaxWaitMillis(3*1000);

// 在应用初始化的时候生成连接池
ShardedJedisPool pool = new ShardedJedisPool(config, shards);

// 在业务操作时,从连接池获取连接
ShardedJedis client = pool.getResource();
try {
// 执行指令
String result = client.set("key-string", "Hello, Redis!");
System.out.println( String.format("set指令执行结果:%s", result) );
String value = client.get("key-string");
System.out.println( String.format("get指令执行结果:%s", value) );
} catch (Exception e) {
// TODO: handle exception
} finally {
// 业务操作完成,将连接返回给连接池
if (null != client) {
pool.returnResource(client);
}
} // end of try block

// 应用关闭时,释放连接池资源
pool.destroy();

运行上述代码,控制台输出:

set指令执行结果:OK
get指令执行结果:Hello, Redis!

⑸ (3)一致性哈希vs哈希取模算法

大数字取模 分散不同桶,两个桶, 2、3、4、5 ,模 2 分桶:

扩容 新桶 ,模 3 来结果:

每次扩展 和 收缩 所有条目分布 重新计算 ,某些场景不可接受。文件 分布 在哪台 哈希算法 决定 ,这个系统想要 加 一台机器时就需要 停 下来等所有文件 重新分布一次 才能对外提供服务,一台机器掉线尽管 只掉一部分数据 ,所有数据访问路由 都会出问题 。 无法平滑的扩缩容 。

无状态化 ,用多瞎轮个桶,  模 7个 ,开始只有两个 3 和 6。同样取模,分到不存在的桶,往下找第一个真实存桶。 2 和 3 分3 桶, 4 和 5分 6 桶。

添加新  编号4桶, 还是模 7 :

3 号桶取模 小于等于 3 ,4 号桶只需从 6 号桶 拿走属于它数字就可, 只调整一个桶重新分布 。即使有 1 亿个桶,增加减少一个桶也只会影响一个桶的数据分布。

只需 和后面机器同步 数据 就可工作 ,同步到后一台机器再下线。 实现中 可以让每台机器 同步一份自己前面机器的数据 ,即使 掉线也不影响 这部分数据服务。

问题:编号  6 的机桶下线 了, 没有后一个桶了 ,哈希空间做成 环状 , 数据给 3  :

一致性哈希还能 实现部分 的分布式系统 无锁化 , 算法的确定性 ,分到哪个桶也是确定的就 不存在争抢 , 不需要分布式锁 了。

查找效率:普通的哈希 查询 一次 哈希计算就可以找到对应的桶了  O(1) ,一致性哈希需要将 排好序 的 桶组成 一个 链表 ,一路找下去k 个桶  O(k)

O(k) 对于哈希来说 不能忍 ,这个量级了用哈希没意义,在排好序桶里查询, 二分 把时间复杂度降到 O(logk) ,桶组合需不断增减,链表实现二分肯定不行,用 跳转表快速跳转 也能 实现 O(logk) 

跳转表中, 每个桶记录距离自己 1,2,4 距离的数字所存的桶,不管查询落在哪个节点上,对整个哈希环上任意的查询一次都 可以至少跳过一半的查询空间? ,这样递归下去很快就可以定位到数据是存在哪个桶上。

上面只是 一种 ,很多一致性哈希 变体 。如选桶:上面顺着数字选 对面 出现 第一个桶 ,其实也可选距离 数字最近桶 ,这样跳转表规则也变。同样跳转表不同算法实现:  CAN,Chord,Tapestry,Pastry 这四种 DHT  

1、如果 6号 (里面有数据)桶突然 宕机 了,是不是里面的数据也丢失了?来不及将桶里的数据往前一个桶移?

答:实际情况会做冗余,画的是一个桶, 实际可能是三个

2、 “一致性” 是扩缩容前后数据在桶里的 分布是一致 的

3、 分布式 系统中怎么 实现服务间的事务 ,目前有哪些通用做法,比如bbo?

用zk或者etcd这种分布式 锁 服务,让接口是 幂等, 可以反复重试

4、增加此类操作会 拖慢集群的性能 。如果某节点上一刻 宕机 ,往后新尺唯数据会进入 下一个节点 ,宕机节点 恢复 ,下一个节点还要同步原属宕机的数据,复杂度为O(nlogn),会不会代价略高?开源的ketama没有同步数据这一做法,所以本质上只是近似一致性哈希,是不是一个trade-off的选择?

答:这个场景不适合使用一致性哈希吧。以 负载均衡 为例,理论上是认为后台服务器是 无状态 的吧。所以宕机重启 不应该有数据同步 的问题。

处理数据同步,raft强一致方案

对磨困信hash结果取余数 (hash() mod N):机器编号从 0到N-1 ,hash()值 按N取模 , 余数i,分发到编号i机器 。致命问题,宕机,落在该机器请求无法处理,有 (N-1)/N 服务器的缓存数据重新计算;为何是 (N-1)/N 呢:3 台机器,hash值 1-6 分布:

host 1: 1 4

host 2: 2 5

host 3: 3 6

挂掉一台,只剩两台, 模数取 2:

host 1: 1 3 5

host 2: 2 4 6

位置不变2个: 1,2,位置改变4个,占共6个数据的比率是 4/6 = 2/3。

N个真实 节点,每个映射成 M个虚拟节 点, M*N个虚拟节点散列在圆环上. 真虚相互交错分布,真down后,平均分到所有节点

访问方法:

写入缓存请求,Key值为K,计算器hash值Hash(K), Hash(K) 对应于环中的某一个点,没有映射到机器节点,顺时针查找,直到找到有映射机器的节点,确定目标节点,超过2^32找不到,命中第一个。

缺陷:server数量很少时,环中分布不均匀,导致cache到server不均匀

例:用电话号码group by,如移动用户多,就会倾斜,reverse或加随机数解决

hash取模对模数有要求,用奇数不用偶数,数据量大的时模数不好选,用上面办法。

https://zhuanlan.hu.com/p/24440059

https://blog.csdn.net/hunandexingkong/article/details/70241933

⑹ 哈希表、哈希算法、一致性哈希表

    散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。它通过把关键码映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数(哈希函数),存放记录的数组叫做散列表。

  优点:

        哈希表可以提供快速的操作。

缺点:

        哈希表通常是基于数组的,数组创建后难于扩展。

        也没有一种简便的方法可以以任何一种顺序〔例如从小到大)遍历表中的数据项 。

    综上, 如果不需要有序遍历数据,井且可以提前预测数据量的大小。那么哈希表在速度和易用性方面是无与伦比的。

        1. 使用哈希函数将被查找的键转换为数组的索引。

        2. 处理哈希碰撞冲突。

    若关键字为 k ,则其值存放在 f(k) 的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系 f 为散列函数,按这个思想建立的表为散列表。

    若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为 均匀散列函数 (Uniform Hash function),这就是使关键字经过散列函数得到一个"随机的地址",从而减少碰撞。

散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快地定位。

一个好的散列函数一般应该考虑下列因素 :

    1.计算简单,以便提高转换速度。

    2.关键词对应的地址空间分布均匀,以尽量减少冲突。

1.   直接寻址法

    取关键字或者关键字的某个线性函数值作为哈希地址,即H(Key)=Key或者H(Key)=a*Key+b(a,b为整数),这种散列函数也叫做自身函数.如果H(Key)的哈希地址上已经有值了,那么就往下一个位置找,直到找到H(Key)的位置没有值了就把元素放进去。

2.   数字分析法

    数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。

3.   平方取中法

    取关键字平方后的中间几位作为散列地址。这种方法的原理是通过取平方扩大差别,平方值的中间几位和这个数的每一位都相关,则对不同的关键字得到的哈希函数值不易产生冲突,由此产生的哈希地址也较为均匀。该方法适用于关键字中的每一位都有某些数字重复出现频度很高的现象。

4.   折叠法

    折叠法是将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(注意:叠加和时去除进位)作为散列地址。

    数位叠加可以有移位叠加和间界叠加两种方法。移位叠加是将分割后的每一部分的最低位对齐,然后相加;间界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加。

    该方法适用于关键字特别多的情况。

5.   随机数法

    选择一个随机数,作为散列地址,通常用于关键字长度不同的场合。

6.   除留余数法

    取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址.即H(Key)=Key MOD p,p<=m.不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选得不好,则很容易产生冲突。

    对不同的关键字可能得到同一散列地址,即 k1≠k2 ,而 f(k1)=f(k2) ,这种现象称为碰撞(英语:Collision)。具有相同函数值的关键字对该散列函数来说称做同义词。

    通过构造性能良好的散列函数,可以减少冲突,但一般不可能完全避免冲突,因此解决冲突是哈希法的另一个关键问题。 创建哈希表和查找哈希表都会遇到冲突,两种情况下解决冲突的方法应该一致。

下面以创建哈希表为例,说明解决冲突的方法。

1.开放寻址法

    这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:Hi=(H(key)+di)%m   i=1,2,…,m-1,其中H(key)为哈希函数,m 为表长,di称为增量序列,i为碰撞次数。增量序列的取值方式不同,相应的再散列方式也不同。增量序列主要有以下几种:

    (1) 线性探测再散列

        di=1,2,3,…,m-1

        这种方法的特点是:冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

    (2)二次探测再散列

        di=12,-12,22,-22,…,k2,-k2( k<=m/2 )

        这种方法的特点是:冲突发生时,在表的左右进行跳跃式探测,比较灵活。

    (3)伪随机探测再散列

        di=伪随机数序列。

    线性探测再散列的 优点 是:只要哈希表不满,就一定能找到一个不冲突的哈希地址,而二次探测再散列和伪随机探测再散列则不一定。线性探测再散列容易产生“二次聚集”,即在处理同义词的冲突时又导致非同义词的冲突。

    其实除了上面的几种方法,开放寻址法还有很多变种,不过都是对di有不同的表示方法。(如双散列探测法:di=i*h2(k))

2.再哈希法

    这种方法是同时构造多个不同的哈希函数:Hi=RHi(key),i=1,2,3,…,n。

    当哈希地址H1=RH1(key)发生冲突时,再计算H2=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。

 3.链地址法(拉链法)

    这种方法的基本思想是将所有哈希地址相同的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表(数组)中,因而查找、插入和删除主要在同义词链中进行。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。链地址法适用于经常进行插入和删除的情况。

     拉链法的优点

        与开放寻址法相比,拉链法有如下几个优点:

            (1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;

            (2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;

            (3)开放寻址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中理论上可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;(散列表的装填因子定义为:α= 填入表中的元素个数 / 散列表的长度)

注:HashMap默认装填因子是0.75。

            (4)在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。而对开放寻址法构造的散列表,删除结点不能简单地将被删结点的空间置为空,否则将截断在它之后填入散列表的同义词结点的查找路径。这是因为各种开放寻址法中,空地址单元都被理解没有查找到元素。 因此在用开放寻址法处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点。

     拉链法的缺点

        拉链法的缺点是:指针需要额外的空间,故当结点规模较小时,开放寻址法较为节省空间,此时将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放寻址法中的冲突,从而提高平均查找速度。

4、建立公共溢出区

    这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表(在这个方法里面是把元素分开两个表来存储)。

    散列表的查找过程基本上和造表过程相同。一些关键码可通过散列函数转换的地址直接找到,另一些关键码在散列函数得到的地址上产生了冲突,需要按处理冲突的方法进行查找。在介绍的三种处理冲突的方法中,产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以,对散列表查找效率的量度,依然用平均查找长度来衡量。

    查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。

影响产生冲突多少有以下三个因素:

    1. 散列函数是否均匀;

    2. 处理冲突的方法;

    3. 散列表的装填因子。

     散列表的装填因子

        定义为:α= 填入表中的元素个数 / 散列表的长度

        α是散列表装满程度的标志因子。由于表长是定值,α与"填入表中的元素个数"成正比,所以,α越大,填入表中的元素较多,产生冲突的可能性就越大;α越小,填入表中的元素较少,产生冲突的可能性就越小。

        实际上,散列表的平均查找长度是装填因子α的函数,只是不同处理冲突的方法有不同的函数。

    这个HASH算法不是大学里数据结构课里那个HASH表的算法。这里的HASH算法是密码学的基础,了解了hash基本定义,就不能不提到一些着名的hash算法,MD5 和 SHA-1 可以说是目前应用最广泛的Hash算法,而它们都是以 MD4 为基础设计的。

Hash算法在信息安全方面的应用主要体现在以下的3个方面:

     ⑴  文件校验

        我们比较熟悉的校验算法有奇偶校验和CRC校验,这2种校验并没有抗 数据篡改 的能力,它们一定程度上能检测出数据传输中的信道误码,但却不能防止对数据的恶意破坏。

        MD5 Hash算法的"数字指纹"特性,使它成为目前应用最广泛的一种文件完整性 校验和 (Checksum)算法,不少Unix系统有提供计算md5 checksum的命令

     ⑵  数字签名

        Hash 算法也是现代密码体系中的一个重要组成部分。由于非对称算法的运算速度较慢,所以在 数字签名 协议中,单向散列函数扮演了一个重要的角色。对 Hash 值,又称"数字摘要"进行数字签名,在统计上可以认为与对文件本身进行数字签名是等效的。而且这样的协议还有其他的优点。

     ⑶ 鉴权协议

        如下的鉴权协议又被称作挑战--认证模式:在传输信道是可被侦听,但不可被篡改的情况下,这是一种简单而安全的方法。

    一致性哈希表简称DHT,主要应用于分布式缓存中,可以用来解决分布式存储结构下动态增加和删除节点所带来的问题。比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N(key是数据的key,N是机器节点数),如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了。

判定哈希算法好坏的四个定义 :

    1、平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用。

    2、单调性(Monotonicity):单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲加入到系统中。哈希的结果应能够保证原有已分配的内容可以被映射到原有的或者新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。

    3、分散性(Spread):在分布式环境中,终端有可能看不到所有的缓冲,而是只能看到其中的一部分。当终端希望通过哈希过程将内容映射到缓冲上时,由于不同终端所见的缓冲范围有可能不同,从而导致哈希的结果不一致,最终的结果是相同的内容被不同的终端映射到不同的缓冲区中。这种情况显然是应该避免的,因为它导致相同内容被存储到不同缓冲中去,降低了系统存储的效率。 分散性的定义就是上述情况发生的严重程度。好的哈希算法应能够尽量避免不一致的情况发生,也就是尽量降低分散性。

    4、负载(Load):负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的, 因此好的哈希算法应能够尽量降低缓冲的负荷。

    在分布式集群中,对机器的添加删除,或者机器故障后自动脱离集群这些操作是分布式集群管理最基本的功能。如果采用常用的hash取模算法,那么在有机器添加或者删除后,很多原有的数据就无法找到了,这样严重的违反了单调性原则。接下来主要说明一下一致性哈希算法是如何设计的。

以SpyMemcached的ketama算法来说,思路是这样的:

把数据用hash函数,映射到一个很大的空间里,如图所示。数据的存储时,先得到一个hash值,对应到这个环中的每个位置,如k1对应到了图中所示的位置,然后沿顺时针找到一个机器节点B,将k1存储到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的数据。由于这些虚拟节点数量很多,均匀分布,因此不会造成“雪崩”现象。

⑺ 一致性哈希算法怎么保证数据的一致性

环割法(一致性 hash)环割法的原理如下:

1. 初始化的时候生成分片数量 X × 环割数量 N 的固定方式编号的字符串,例如 SHARD-1-NODE-1,并计算所有 X×N 个字符串的所有 hash 值。

2. 将所有计算出来的 hash 值放到一个排序的 Map 中,并将其中的所有元素进行排序。

3. 输入字符串的时候计算输入字符串的 hash 值,查看 hash 值介于哪两个元素之间,取小于 hash 值的那个元素对应的分片为数据的分片。

数据比较

下面将通过测试对环割法和跳跃法的性能及均衡性进行对比,说明 DBLE 为何使用跳跃法代替了环割法。

⑻ 一致性哈希 java实现 怎么映射到圆环上

一致性哈希提出了在动态变化的Cache环境中,哈希算法应该满足的4个适应条件:单调性是指如果已经有一些内容通过哈希分派到了相应的缓冲中,又有新的缓冲区加入到系统中,那么哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲区中去,而不会被映射到旧的缓冲集合中的其他缓冲区。(这段翻译信息有负面价值的,当缓冲区大小变化时一致性哈希(Consistenthashing)尽量保护已分配的内容不会被重新映射到新缓冲区。)简单的哈希算法往往不能满足单调性的要求,如最简单的线性哈希:x→ax+bmod(P)在上式中,P表示全部缓冲的大小。不难看出,当缓冲大小发生变化时(从P1到P2),原来所有的哈希结果均会发生变化,从而不满足单调性的要求。哈希结果的变化意味着当缓冲空间发生变化时,所有的映射关系需要在系统内全部更新。而在P2P系统内,缓冲的变化等价于Peer加入或退出系统,这一情况在P2P系统中会频繁发生,因此会带来极大计算和传输负荷。单调性就是要求哈希算法能够应对这种情况。负载问题实际上是从另一个角度看待分散性问题。既然不同的终端可能将相同的内容映射到不同的缓冲区中,那么对于一个特定的缓冲区而言,也可能被不同的用户映射为不同的内容。与分散性一样,这种情况也是应当避免的,因此好的哈希算法应能够尽量降低缓冲的负荷。从表面上看,一致性哈希针对的是分布式缓冲的问题,但是如果将缓冲看作P2P系统中的Peer,将映射的内容看作各种共享的资源(数据,文件,媒体流等),就会发现两者实际上是在描述同一问题。路由算法在一致性哈希算法中,每个节点(对应P2P系统中的Peer)都有随机分配的ID。在将内容映射到节点时,使用内容的关键字和节点的ID进行一致性哈希运算并获得键值。一致性哈希要求键值和节点ID处于同一值域。最简单的键值和ID可以是一维的,比如从0000到9999的整数集合。根据键值存储内容时,内容将被存储到具有与其键值最接近的ID的节点上。例如键值为1001的内容,系统中有ID为1000,1010,1100的节点,该内容将被映射到1000节点。为了构建查询所需的路由,一致性哈希要求每个节点存储其上行节点(ID值大于自身的节点中最小的)和下行节点(ID值小于自身的节点中最大的)的位置信息(IP地址)。当节点需要查找内容时,就可以根据内容的键值决定向上行或下行节点发起查询请求。收到查询请求的节点如果发现自己拥有被请求的目标,可以直接向发起查询请求的节点返回确认;如果发现不属于自身的范围,可以转发请求到自己的上行/下行节点。为了维护上述路由信息,在节点加入/退出系统时,相邻的节点必须及时更新路由信息。这就要求节点不仅存储直接相连的下行节点位置信息,还要知道一定深度(n跳)的间接下行节点信息,并且动态地维护节点列表。当节点退出系统时,它的上行节点将尝试直接连接到最近的下行节点,连接成功后,从新的下行节点获得下行节点列表并更新自身的节点列表。同样的,当新的节点加入到系统中时,首先根据自身的ID找到下行节点并获得下行节点列表,然后要求上行节点修改其下行节点列表,这样就恢复了路由关系。

阅读全文

与一致性哈希算法java相关的资料

热点内容
dvd光盘存储汉子算法 浏览:757
苹果邮件无法连接服务器地址 浏览:963
phpffmpeg转码 浏览:672
长沙好玩的解压项目 浏览:145
专属学情分析报告是什么app 浏览:564
php工程部署 浏览:833
android全屏透明 浏览:737
阿里云服务器已开通怎么办 浏览:803
光遇为什么登录时服务器已满 浏览:302
PDF分析 浏览:486
h3c光纤全工半全工设置命令 浏览:143
公司法pdf下载 浏览:382
linuxmarkdown 浏览:350
华为手机怎么多选文件夹 浏览:683
如何取消命令方块指令 浏览:350
风翼app为什么进不去了 浏览:779
im4java压缩图片 浏览:362
数据查询网站源码 浏览:151
伊克塞尔文档怎么进行加密 浏览:893
app转账是什么 浏览:163