Redis Cluster集群hash tag原理分析
Redis Cluster集群hash tag原理分析
ivansli工欲善其事必先利其器,在正式开始研究redis cluster hash tag之前,我们先以最小的成本搭建一套cluster集群。
docker-compose搭建redis cluster
这里使用dockerhub的bitnami/redis-cluster
进行搭建,过程相比网上其他教程而言非常简单,2个shell命令搞定。
1 | curl -sSL https://raw.githubusercontent.com/bitnami/containers/main/bitnami/redis-cluster/docker-compose.yml > docker-compose.yml |
bitnami redis-cluster 对应的 docker-compose.yml (笔者做了细微的修改)
1 | version: '3' |
看到如下所示,即可认为redis cluster集群成功启动。
1 | ➜ ivansli$ docker-compose up -d |
可以看到redis cluster集群启动成功,其架构为:3主(master)3从(slave)。
cluster master节点 | slot范围 |
---|---|
master1 | 0-5460 |
master2 | 5461-10922 |
master3 | 10923-16383 |
集群共有16384(0~16383,2^14个)个slot(哈希槽),每个master节点拥有连续的一段slot。
redis cluster集群key的存储方式
众所周知,对于单台服务器来说,在计算机硬件不变更的情况下其资源空间存在上限,例如:内存、硬盘、网卡带宽、CPU核心等。对于基于内存的redis服务来说,想要存储更多的、超过单机内存容量的数据,那么只能采用集群模式,最常用的就是redis cluster集群。
其数据存储原理简化为公式:CRC16(key) % 16384 = slot
,即:取key进行CRC16计算之后对16384取模运算得到key所在的slot,由于redis cluster在启动时会对每一台master节点分配slot空间,那么当前slot的值在哪台master节点的slot空间范围内,key就存储在哪台节点。
通俗的讲:使用一种算法把整个数据空间化整为零分散存储在多个节点 (分布式系统设计中常用策略)。
什么是hash tag
Redis官方对Hash tags的定义如下:
There is an exception for the computation of the hash slot that is used in order to implement hash tags. Hash tags are a way to ensure that multiple keys are allocated in the same hash slot. This is used in order to implement multi-key operations in Redis Cluster.
To implement hash tags, the hash slot for a key is computed in a slightly different way in certain conditions. If the key contains a “{…}” pattern only the substring between { and } is hashed in order to obtain the hash slot. However since it is possible that there are multiple occurrences of { or } the algorithm is well specified by the following rules:
- IF the key contains a { character.
- AND IF there is a } character to the right of {.
- AND IF there are one or more characters between the first occurrence of { and the first occurrence of }.
Then instead of hashing the key, only what is between the first occurrence of { and the following first occurrence of } is hashed.
hash tags 是用于计算哈希槽时的一个例外,是一种确保多个键分配到同一个哈希槽中的方法
。这是为了在Redis集群中实现多键操作而使用的。
为了实现hash tags,在某些情况下,会以稍微不同的方式计算key的哈希槽。如果key只包含”{…}”模式,则仅对{和}之间的子字符串进行散列以获取哈希槽。但由于可能存在多个{或}出现,因此该算法遵循以下规则:
- 如果key包含字符 {
- 并且如果 } 字符位于 { 的右侧
- 并且在第一个 { 和第一个 } 之间存在一个或多个字符
对于符合上述规则的key,则不会对整个key进行散列处理,而只会对第一次出现 { 和随后第一次出现 } 之间的内容进行散列。否则,对整个key进行散列处理。
为什么使用hash tag
不使用hash tag批量获取不同名称的key
1 | 127.0.0.1:6379> mget name name1 name2 name3 |
显示错误信息:CROSSSLOT 请求中的key没有哈希到同一个插槽
。错误在指令执行之前的检查中触发,代码逻辑如下所示:
1 | // 代码文件:src/server.c |
使用hash tag批量获取不同名称的key
1 | 172.26.0.5:6379> mget name {name} {name}1 {name}2 {name}3 |
显示正常(请求中的key被哈希到同一个插槽)。
对上述操作的key计算得出对应slot,整理如下所示:
key | 计算key的slot | slot值 | slot所在节点 | 是否使用hash tag |
---|---|---|---|---|
name | cluster keyslot name | 5798 | master2 | ❎ |
name1 | cluster keyslot name1 | 12933 | master3 | ❎ |
name2 | cluster keyslot name2 | 742 | master1 | ❎ |
name3 | cluster keyslot name3 | 4807 | master1 | ❎ |
{name} | cluster keyslot {name} | 5798 | master2 | ✅ |
{name}1 | cluster keyslot {name}1 | 5798 | master2 | ✅ |
{name}2 | cluster keyslot {name}2 | 5798 | master2 | ✅ |
{name}3 | cluster keyslot {name}3 | 5798 | master2 | ✅ |
redis中使用
cluster keyslot key的名称
可以得到key对应的slot值
结合上面例子以及官方对hash tag的描述,想必大家已经基本可以对为什么使用hash tag
这个问题得出自己的结论。
假如在开发过程中,遇到Redis中既要存储大量数据,又要让某些相同特征的key(key包含相同字符串)存储在同一个节点的情况。那么,这个时候Redis cluster+hash tag绝对是你的首选。
redis源码中hash tag的计算方式
获取key的hash tag值,主要包含2部分逻辑:
- 查找 {} 包含的字符串
- 对找到的字符串进行使用crc16()计算
具体代码实现如在所示:
1 | // 代码文件:src/cluster.c |
hash tag 可能导致的问题
在Redis cluster集群中,使用hash tag可以确保多个键分配到同一个哈希槽中。但是,以下几种情况可能会导致一些负面影响:
- 存储时,大量key使用hash tag后落到同一slot,slot所在节点导致存储大量数据,甚至超过内存上限(数据倾斜)
- 查询/删除时,大量key使用hash tag后落到同一slot,slot所在节点处理大量请求,导致服务器忙碌、响应失败,甚至宕机(hot key)
hash tag是一把双刃剑,在使用时需要考虑具体业务逻辑与场景,应当尽量避免上述问题。假设无法避免时,可以对key按照业务线或者场景进行细化,进而对key进行拆分,以便更均匀的存储在不同的slot上。
延伸阅读
dockerhub bitnami/redis-cluster
Redis cluster specification
Redis hash tag 如何運作?
Redis Cluster mode and Key distribution