redis cluster集群 slot 计算方法
一条 redis 命令或一个 lua 脚本发送到 redis 集群后,如何计算具体在哪一台 redis 实例去执行命令,以及如何指定在某一台 redis 去执行呢?
答案是slot(槽),每个 redis 集群都会有 16384 个slot,这些 slot 会分布在所有 master 节点中。
一、如何计算 slot ?
对键通过 执行 crc16 算法,再对 16384 取余,即可得到一个小于 16384 的数,该结果就是 slot,哪个 slot 由哪个 redis 节点管理,是在集群启动后就已经确定了的(集群启动后也能进行槽迁移,如线上动态横向扩展 redis 节点的时候,槽迁移完毕后也确定了 slot 和 redis 的映射)。
在 Java 中,可通过以下算法计算出slot:
import io.lettuce.core.codec.CRC16;
public class Test {
public static void main(String[] args){
String str = "test";
int slot = CRC16.crc16(str.getBytes()) % 16384;
System.out.println("slot = " + slot);
}
}
输出结果:
slot = 6918
在 redis 集群验证:
访问 key 为 test 的键,定位到槽 [6918] 所在的节点 7001,并转到该节点执行命令,返回OK
二、如何指定在某一台 redis 去执行命令?
与其说指定在某一台 redis 去执行命令,不如说是如何指定 redis 键的slot。
这里用到了 redis 的键哈希标签原理,redis 在计算 slot 的时候,有以下代码:
如果有完整的 {},则只会将 {} 内部的字符串进行 slot 的计算
unsigned int keyHashSlot(char *key, int keylen) {
int s, e;
for (s = 0; s < keylen; s++)
if (key[s] == '{') break;
if (s == keylen) return crc16(key,keylen) & 0x3FFF;
for (e = s+1; e < keylen; e++)
if (key[e] == '}') break;
if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
return crc16(key+s+1,e-s-1) & 0x3FFF;
}
验证:
计算 {test}stra 和 {sest}strb 两个不同字符串的 slot,结果都是 6918
结尾:关于 lua 脚本,由于现阶段 redis (当前最高版本6.2.1)执行命令或 lua 脚本都是通过单线程执行,因此 lua 脚本的执行可以保证原子性,不存在并发。正是这个原因,对 lua 脚本的要求就是,所有 key 都必须映射到当前节点,否则会抛出 RedisCommandExecutionException 异常。
io.lettuce.core.RedisCommandExecutionException: CROSSSLOT Keys in request don't hash to the same slot
ps:以后 redis 可能会并发执行命令,且关注吧,毕竟多线程执行的效率比单线程高得多。当前 redis 执行命令是单线程,但是其他操作基本都是多线程的。
推荐阅读
-
php成功操作redis cluster集群的实例教程
-
Redis Cluster(集群)
-
redis集群错误解决:/usr/lib/ruby/gems/1.8/gems/redis-3.0.0/lib/redis/client.rb:79:in `call': ERR Slot 15495 is already busy (Redis::CommandError)
-
使用Ruby脚本部署Redis Cluster集群步骤讲解
-
Redis集群模式下的redis-py-cluster方式读写测试
-
redis-cluster集群安装(基于redis-3.2.10)
-
Redis Cluster集群动态扩容的实现
-
Redis的Cluster集群搭建的实现步骤
-
Redis Cluster集群主从切换的踩坑与填坑
-
Redis Cluster 集群搭建你会吗