Redis bigkey解决方案

什么是大key

查询bigkey

集群模式查看bigkey

redis-cli排查

cluster模式排查大key
因为clister集群模式下查询bigkey时,因为键会分散在不同的槽(slot)和不同的节点上,因此需要分别连到各个主节点进行检查,或者在命令添加-c参数
首先需要查看cluster各个节点,连接其中任意一个节点执行以下命令

CLISTER NODES

查看集群中每个节点的信息,返回结果如下,是以空格分割的CSV格式

07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004,hostname4 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected
67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002@31002,hostname2 master - 0 1426238316232 2 connected 5461-10922
292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 127.0.0.1:30003@31003,hostname3 master - 0 1426238318243 3 connected 10923-16383
6ec23923021cf3ffec47632106199cb7f496ce01 127.0.0.1:30005@31005,hostname5 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 0 1426238316232 5 connected
824fe116063bc5fcf9f4ffd895bc17aee7731ac3 127.0.0.1:30006@31006,hostname6 slave 292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 0 1426238317741 6 connected
e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 127.0.0.1:30001@31001,hostname1 myself,master - 0 0 1 connected 0-5460

每行格式如下:

<id> <ip:port@cport[,hostname]> <flags> <master> <ping-sent> <pong-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>

其中可以区分是否主从节点
master 主节点
slave 从节点
myself 当前节点
fail 当前节点处于失恋状态
如果是从节点,展示主节点的ID,如果是主节点,则显示-,可以看出谁是谁的主节点,谁是谁的从节点
表示当前节点的hash槽
以三主三从为例可以看到当前集群的节点信息

通过以上获取到各个节点信息,可以查看每个节点的bigkey情况
执行命令

redis-cli -h 节点ip -p 端口 --bigkeys

返回的并非就是bigkey, 而是每个类型的top1,同时给出每种数据类型的键值个数和平均大小, 大致衡量大小为:string类型大于10kb,hash,zset,list,set元素个数超过5000个,
以三主三从为例,如果卡槽为0-5460、5461-10922可能会出现以下提示

意味着访问的key被移到另一卡槽了可以手动重定向到提示的MOVE节点进行尝试

除了上述方法,也可以使用下面命令查看某个key的大小

MEMORY USAGE key [SAMPLES count]

返回结果为所占字节大小,如果key不存在resps2返回nil,resp3返回null

解决方案

如何是key的名称较大,可以使用md5进行摘要算法生成固定的长度

如果是value较大,可以拆分为多个子key,比如一个大的list,拆分成多个小的list

再利用管道操作可以一定程度上保证事务

public void saveBigKey() {
        ArrayList<Object> list = new ArrayList<>();
        List<List<Object>> partList = Lists.partition(list, 100);
        String masterKey = "master_key";
        redisTemplate.executePipelined((RedisCallback<Object>) con -> {
            con.del(rawKey(masterKey));
            for (int i = 0; i < partList.size(); i++) {
                String partKey = String.format("CHILD_KEY_%s", i);
                con.set(rawKey(partKey), rawValue(JsonUtil.dumpObject(partList.get(i))));
                con.expire(rawKey(partKey), TimeUnit.DAYS.toSeconds(2));
                con.zAdd(rawKey(masterKey), i, rawValue(partKey));
            }
            con.expire(rawKey(masterKey), TimeUnit.DAYS.toSeconds(1));
            return null;
        });
    }

    RedisSerializer keySerializer() {
        return redisTemplate.getKeySerializer();
    }

    RedisSerializer valueSerializer() {
        return redisTemplate.getValueSerializer();
    }

    @SuppressWarnings("unchecked")
    byte[] rawKey(Object key) {

        Assert.notNull(key, "non null key required");

        if (keySerializer() == null && key instanceof byte[]) {
            return (byte[]) key;
        }

        return keySerializer().serialize(key);
    }

    @SuppressWarnings("unchecked")
    byte[] rawValue(Object value) {

        if (valueSerializer() == null && value instanceof byte[]) {
            return (byte[]) value;
        }

        return valueSerializer().serialize(value);
    }
posted @ 2024-04-11 19:19  木马不是马  阅读(44)  评论(0编辑  收藏  举报