欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

redis cluster集群 slot 计算方法

程序员文章站 2022-07-05 20:37:32
...

一条 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 cluster集群 slot 计算方法

在 redis 集群验证:

访问 key 为 test 的键,定位到槽 [6918] 所在的节点 7001,并转到该节点执行命令,返回OK

redis cluster集群 slot 计算方法

二、如何指定在某一台 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

redis cluster集群 slot 计算方法

redis cluster集群 slot 计算方法

 

结尾:关于 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 执行命令是单线程,但是其他操作基本都是多线程的。