Redis和memcache在分布式集群中如何通过key寻址

468 阅读6分钟

Redis和memcache在分布式集群中如何通过key寻址?Redis和memcache分别采用了一致性 hash 算法和hash slot 算法进行寻址,本文将介绍常见的三种常用的分布式寻址算法的原理及其应用。

概述

常见分布式寻址算法主要有三种:

  1. hash 算法(余数Hash算法)

  2. 一致性 hash 算法

  3. hash slot 算法

下面介绍一下每种hash算法的原理以及应用场景。

余数Hash算法

原理

  1. 通过md5或者其他算法将key编码

  2. 取其中几位通过hash算法将可以处理成一个整数

  3. 对服务器数量取模,映射到其中某一台memcache服务器

image.png

优点

如果不考虑服务器集群的伸缩性,余数Hash算法几乎可以满足绝大多数的缓存路由需求

缺点

当分布式缓存集群需要扩容的时候,会导致缓存命中率急剧下降

命中率问题

原来有n台服务器:

删掉一台服务器,缓存命中率会变为原来的1/(n-1)

新增一台服务器,缓存命中率会变为原来的1/(n+1)

一致性hash算法

原理

原始的一致性hash算法通过md5或者其他算法将key编码,通过hash算法处理成一个32位整数,映射到一个环( hash空间)上,如图:

image.png

但是这样在增删节点的时候可能会带来一些问题,删除某个节点,比如图中黄色段对应的node2节点删除,那么node3节点的压力就会剧增,因为原来node2节点的流量会全部打到node3节点,增加或者删除节点时,它影响的范围小, 但是会导致部分节点压力剧增。

那么有没有什么好的办法能够解决部分节点压力剧增的问题呢?有,那就是在hash环上增加虚拟节点。为每个节点增加均匀分布的虚拟节点,虚拟节点的本质是同一节点在hash空间上的复制品。增加虚拟节点后的hash环如图:

image.png

这样一来,假如删除node1节点,原来打到node1节点的请求会被分散到node2、node3,就能避免上面全部请求打到一个节点的问题。

应用

memcached的寻址算法

哈希槽(hash slot)算法

原理

一致性哈希算法对于数据分布、节点位置的控制并不是很友好。Redis Cluster在设计中没有使用一致性哈希(Consistency Hashing),而是使用数据分片引入哈希槽(hash slot)来实现;

下面介绍hash slot算法就都结合其在redis cluster的应用来说了。

一个 Redis Cluster包含16384(0~16383)个哈希槽(补充:为什么redis集群的最大槽数是16384个?),


为什么是163842^14)个?

在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 16K),也就是说使用2k的空间创建了16k的槽数。

虽然使用CRC16算法最多可以分配655352^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) =65K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。

存储在Redis Cluster中的所有键都会被映射到这些slot中,集群中的每个键都属于这16384个哈希槽中的一个。按照槽来进行分片,通过为每个节点指派不同数量的槽,可以控制不同节点负责的数据量和请求数.

当前集群有3个节点,槽默认是平均分的:

  • 节点 A (6381)包含 0 到 5499号哈希槽.

  • 节点 B (6382)包含5500 到 10999 号哈希槽.

  • 节点 C (6383)包含11000 到 16383号哈希槽.

image.png (图片借用)

哈希槽计算公式

集群使用公式slot=CRC16(key)/16384来计算key属于哪个槽,其中CRC16(key)语句用于计算key的CRC16 校验和。

哈希槽怎么工作

我们看到的是master节点在 Redis Cluster中的实现时,都存有所有的路由信息。

当客户端的key 经过hash运算,发送slot 槽位不在本节点的时候:

(1)如果是非集群方式连接,则直接报告错误给client,告诉它应该访问集群中那个IP的master主机。

(2)如果是集群方式连接,则将客户端重定向到正确的节点上

image.png

哈希槽的优点

很容易添加或者删除节点:

如果想新添加个节点D, 我需要从节点 A, B, C中转移部分槽到D上即可.

如果移除节点A,只需要将A中得槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可.

由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.

查看哈希槽分区情况

通过cluster nodes命令,可以清晰看到Redis Cluster中的每一个master节点管理的哈希槽。比如 127.0.0.1:7001 拥有哈希槽 0-5460, 127.0.0.1:7002 拥有哈希槽 5461-10922, 127.0.0.1:7003 拥有哈希槽 10923-16383。


//查看集群中,各个master节点的哈希槽分区情况

127.0.0.1:7001> cluster nodes

e51711eb03d 127.0.0.1:7002@17002 master - 0 1590126183862 2 connected 5461-10922

68c5fc14287 127.0.0.1:7003@17003 master - 0 1590126181856 3 connected 10923-16383

903322e4431 127.0.0.1:7001@17001 myself,master - 0 1590126182000 1 connected 0-5460