编辑
2023-03-27
缓存中间件
0
请注意,本文编写于 404 天前,最后修改于 232 天前,其中某些信息可能已经过时。

目录

在Redis集群中{user}:123和{user}:456两个key会被放到同一个槽中吗?

在Redis集群中{user}:123和{user}:456两个key会被放到同一个槽中吗?

答案是会的。

在Redis集群中,数据是通过哈希槽来分片的。每个槽都有一个编号,从0到16383。当Redis集群中有多个节点时,每个节点负责处理一部分槽。当一个客户端向Redis集群中写入一个键值对时,Redis会根据键名计算出一个哈希值,然后将这个哈希值对16384取模,得到一个槽编号。Redis会根据这个槽编号将键值对分配到相应的节点上。

在Redis集群中,如果两个键名被分配到同一个槽上,它们就会被放到同一个节点上。因此,如果两个键名的哈希值对16384取模的结果相同,它们就会被放到同一个槽中。

Redis计算槽位的算法

Redis计算槽位的算法是将键名计算出一个哈希值,然后将这个哈希值对16384取模,得到一个槽编号。具体来说,Redis使用的哈希函数是MurmurHash2算法,它可以将任意长度的数据映射为一个128位的哈希值。在计算键名的哈希值时,Redis只使用了其中的一部分,具体来说是使用了128位哈希值的低16位和高16位的异或值。这样可以保证哈希值的分布比较均匀,从而避免出现热点数据。

在Python中,可以使用redis-py库中的crc16函数来计算Redis键名的哈希值。具体来说,可以按照以下步骤计算哈希值:

将键名转换为字节串。 计算字节串的CRC16校验和。 将校验和对16384取模,得到槽编号。

下面是一个示例代码:

python
import zlib def get_slot(key): # 将键名转换为字节串 key_bytes = key.encode('utf-8') # 计算字节串的CRC16校验和 crc = zlib.crc32(key_bytes) & 0xffffffff # 将校验和对16384取模,得到槽编号 return crc % 16384

注意,这里使用了zlib库中的crc32函数来计算CRC16校验和。因为Redis使用的CRC16算法和zlib库中的CRC32算法类似,都是多项式除法的形式,所以可以使用zlib库中的crc32函数来计算CRC16校验和。

有同学可能会有疑问,{user}:123和{user}:456两个key计算结果不一样啊?

咱们去Redis源码中去解密一下。 Redis计算槽位的源码可以在Redis的源代码中找到。具体来说,在Redis的src/cluster.c文件中,有一个名为clusterHashSlot函数,它实现了计算槽位的算法。下面是该函数的源代码:

c
unsigned int clusterHashSlot(const char *key, int keylen) { int s, e; /* start-end indexes of { and } */ /* Search the first occurrence of '{'. */ for (s = 0; s < keylen; s++) if (key[s] == '{') break; /* No '{' ? Hash the whole key. This is the base case. */ if (s == keylen) return crc16(key,keylen) & 0x3FFF; /* '{' found? Check if we have the corresponding '}'. */ for (e = s+1; e < keylen; e++) if (key[e] == '}') break; /* No '}' or nothing betweeen {} ? Hash the whole key. */ if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF; /* If we are here there is both a { and a } on its right. Hash * what is in the middle between { and }. */ return crc16(key+s+1,e-s-1) & 0x3FFF; }

可以看到,该函数首先搜索键名中的第一个左花括号,然后搜索第一个右花括号,并计算它们之间的子串的哈希值。如果没有找到左花括号或右花括号,或者它们之间没有字符,就计算整个键名的哈希值。最后,将哈希值对16384取模,得到槽编号。所以现在明白了吧,如果Redis的key中有{}包起来的内容,只要其中内容相同就能保证能算出来一样的槽编号。{}包起来的内容被称为"Hash Tag"。 关于Hash Tag介绍可以参见官方文档:https://redis.io/docs/reference/cluster-spec/#hash-tags

注意,这里使用了crc16函数来计算哈希值。crc16函数是Redis自己实现的一个CRC16算法,它可以将任意长度的数据映射为一个16位的哈希值。

本文作者:whitebear

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!