redis集群模式下,希望在多个slot都建立副本数据,key怎么设计?

比如现在redis有3个集群分片(A、B、C),
有一个高频热点key,保存在分片A上,会导致请求的倾斜,使得A分片的读取压力过大。

现在希望将key设计成:key1、key2、key3,都对应相同的value
存储的时候:
key1存在分片A
key2存在分片B
key3存在分片C

查询的时候:
key随机转化成key1、key2、key3的其中一个,然后路由到某个分片上。

key1、key2、key3的值改怎么设计?
开发人员怎么知道一个key对应的哈希值是多少

阅读 1.2k
3 个回答

最近看了一下docker搭建redis集群的知识,我来自己回答一下这个问题吧。

哈希槽原理

首先redis cluster会创建[0,16383]哈希槽,供多个集群分片使用。
当set Key Value保存数据时,redis会使用★【CRC16算法】★,计算key的hash值。
具体公式 : 槽位 = CRC16.crc16(key) % 16384

(具体算法可参考jar包【io.lettuce:lettuce-core】中的SlotHash.getSlot("Key")方法,只需要项目依赖jar包【spring-boot-starter-data-redis】,就可以引入lettuce的核心组件)

后缀拼接序号方案(行不通)

所以直接在将key拼接数字后缀,形成key-1,key-2,key-3,最终形成的hash并不能保证分别落在不同的分区哈希槽上,来降低热点数据key在单个节点的负载。参考如下代码:

import io.lettuce.core.cluster.SlotHash;

public class TestHashSlot {
    public static void main(String[] args) {
        /*
        redis集群中3个主节点的槽位
        分片1:[0,5460]
        分片2:[5461,10922]
        分片3:[10923,16383]
         */
        String mykey = "Key-Test";
        System.out.println(SlotHash.getSlot(mykey+"01"));// 6582     插入到分片2
        System.out.println(SlotHash.getSlot(mykey+"02"));// 10709    插入到分片2
        System.out.println(SlotHash.getSlot(mykey+"03"));// 14836    插入到分片3
    }
}

综上,通过key拼接数字后缀并不能在redis集群模式下,实现副本key在多个节点上的平均分布。

其他解决方案:

【方案1】引入额外的redis服务单独存储热点数据

此时:

  • 第一部分:redis使用集群模式,通过分配哈希槽,来实现海量key的分布式存储
  • 第二部分:redis建立多个独立的主从,每个主从中,都具有相同的热点数据副本,插入时全部插入,读取时随机读取一个。

【方案2】本地缓存法(即二级缓存)

直接使用本地缓存,比如ehcache,当读取高频热点数据时,直接内存中读取。可以设置较短的过期时间,只要数据一直高频访问,就不会过期。在高频读取场景下,节省了reids的网络io成本,更推荐

应用层解决方法:

ORIGINAL_KEY="some_hot_key"
VALUE="some_value"

KEY_SUFFIXES=(1 2 3)
KEYS=()

for SUFFIX in "${KEY_SUFFIXES[@]}"; do
    KEY="${ORIGINAL_KEY}_${SUFFIX}"
    SLOT=$(redis-cli CLUSTER KEYSLOT "$KEY")
    echo "Key $KEY is in slot $SLOT."
    KEYS+=("$KEY")
done

# 存储值到所有键
for KEY in "${KEYS[@]}"; do
    redis-cli SET "$KEY" "$VALUE"
done

SELECTED_KEY=${KEYS[$RANDOM % ${#KEYS[@]}]}
VALUE=$(redis-cli GET "$SELECTED_KEY")
echo "The value of $SELECTED_KEY is '$VALUE'."

你可以这样弄,比如redis key 为 recommend,总共分3片,你 recommend-1,recommend-2,recommend-3, 你redis set的时候,同时 set 这三个key, get 时

// 伪代码

getRecommend(){
// 先随机出1-3一个数 
Random random = new Random();
int randomNumber = random.nextInt(3) + 1;
// 然后就可以随机出个key取值了
String cacheKey = "recommend-"+ randomNumber;


}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏