目录
hash算法
Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
--摘抄自百度百科
简单理解就是按照某种算法散列得到一个值。
redis cluster集群就是采用这的CRC16算法根据key计算分配存储的卡槽位置,然后找到属于哪个节点分配的卡槽范围进行存储。
如下图,假设采用三个master节点喝三个slave节点的集群方式。卡槽节点分配如下:
msater1分配的卡槽是12000-16284;
msater2分配的卡槽是6001-12000;
msater3分配的卡槽是0-6000。
redis处理流程:
- 业务系统上线前预热cache初始化一个热点key:(xxx-detailPage, "xxxx"); redis通过CRC16(xxx-detailPage) == 16算法得到16的卡槽;
- master节点之间会同步卡槽信息,16384个卡槽占用16384 /1024/8 = 2kb的大小,还有节点的其他信息等。这样master节点就可以感应卡槽范围,连接master1时可以将key路由到master3进行存储;
- master3节点将数据写入并持久化,然后同步数据给slave节点;
一致性hash算法
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 [2] 。
一致性哈希算法将整个哈希值空间映射成一个虚拟的圆环,整个哈希空间的取值范围为0~232-1。整个空间按顺时针方向组织。0~232-1在零点中方向重合。接下来使用如下算法对服务请求进行映射,将服务请求使用哈希算法算出对应的hash值,然后根据hash值的位置沿圆环顺时针查找,第一台遇到的服务器就是所对应的处理请求服务器。
--摘抄自百度百科
我们采用一致性hash算法设计一下redis cluster集群 ,可以和采用hash算法对比一下。
- 为了和卡槽对应采用0-16384组成hash环,hash算法同样采用CRC16算法;
- 同样采用三个master节点和三个slave节点的方式;
- master3分配的卡槽是12000-16284,msater2分配的卡槽是6001-12000,msater1分配的卡槽是0-6000。
处理流程:
- 同样的redis通过CRC16(key) == 16算法得到hash值为16;
- hash值落入0-6000范围,然后顺势正找到第一个服务节点master1;
- master1节点将数据写入并持久化,然后同步数据给slave节点;
但是这种直接路由到物理节点有很大的缺陷,按照hash算法的特性我们将环均分3段,那么每个点落在这三段的概率是均等的。但是在增减节点时就会造成数据倾斜,比如将master2的节点去掉之后master1和master3不能再满足均匀地获取到数据条件。此时需要再平衡,重新划分段,导致大量的数据迁移。
解决方法:设置虚拟节点
例如master1、master2、master3分别设置10个虚拟节点。
master1(a1,a2,...a10)
master2(b1,b2,...b10)
master3(c1,c2,...c10)
根据hash算法特性可以知道,每个hash值落在黄色段中的概率是相等的。
master1、master2的虚拟节点如下:
为什么redis不采用一致性hash算法
看上面的两种方法,基本没有太大的区别。数据来了hash运算,然后路由到不同的节点。但是有一个比较明显的区别,就是当一个节点挂掉后数据的路由策略。
差异:
1. 假如node1因为热点key问题宕机了,然后选取slave1作为主节点,那么salve也会承受不了压力进而宕机。由于一致性hash算法,那么master1被剔除了,它的请求就会路由到master2,然后master2也会宕机,然后master3也会宕机,导致缓存的雪崩效应。
2. 采用hash算法,master3因为热点key宕机掉了也只会影响0-6000的key的访问。而且运维人员可以手动处理故障,将热点的卡槽分配到性能更好的机器上。
我认为这是采用hash算法而不是采用hash一致性算法的最大原因。