Redis-键值设计

发布于:2024-04-23 ⋅ 阅读:(20) ⋅ 点赞:(0)

Redis-键值设计

1.设置key的规范

  1. 遵循基本格式:【业务名称】:【数据名】:【id】 可读性强,在客户端的情况下使用:如果前缀相同会分目录层级
  2. 长度不超过44字节 string数据结构的三种类型,在44字节之内是embstring 内存占用小
  3. 不包含特殊字符

优点

  • 可读性强
  • 避免key冲突
  • 方便估计案例
  • 更节省内存

2.BigKey

什么是BigKey

在redis中存储的value空间大,导致效率变低。

BigKey的危害
  1. 网络阻塞
    对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,网络速度变慢

  2. 数据倾斜
    BigKey所在的Redis示例内存使用率远超其他示例

  3. Redis阻塞

    对元素较多的hash,list,zset,set做运算会耗时

  4. CPU压力
    对BigKey的数据序列化和反序列化会导致cpu的使用飙升

如何排查BigKey
  1. redis-cli-bigkeys

  2. scan扫描 Java代码示例
    引入pom

    <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>3.8.0</version> <!-- 替换为最新的稳定版本 -->
            </dependency>
    
    /**
     * 排查大key
     *
     */
    @SpringBootTest
    public class RedisScanBigKey {
    
        /**
         * 字符串的大key
         */
        final static int STR_MAX_LEN = 10 * 1024;
        /**
         * 集合的大key
         */
        final static int HASH_MAX_LEN = 500;
    
        @Test
        public void scanBigKey() {
            Jedis jedis = new Jedis("localhost", 6379);
            int maxLen = 0;
            long len = 0;
            // 开始游标,首次调用传入0
            String cursor = "0";
    
            do {
                // 执行SCAN命令,传入游标、匹配模式(可选)和COUNT(可选)
                ScanResult<String> scanResult = jedis.scan(cursor);
    
                cursor = scanResult.getCursor();
                // 处理本次扫描返回的元素
                List<String> list = scanResult.getResult();
                if (CollectionUtil.isEmpty(list)) {
                    break;
                }
                // 遍历
                for (String key : list) {
                    String type = jedis.type(key);
                    switch (type) {
                        case "string":
                            len = jedis.strlen(key);
                            maxLen = STR_MAX_LEN;
                            break;
                        case "hash":
                            len = jedis.hlen(key);
                            maxLen = HASH_MAX_LEN;
                            break;
                        case "list":
                            len = jedis.llen(key);
                            maxLen = HASH_MAX_LEN;
                            break;
                        case "set":
                            len = jedis.scard(key);
                            maxLen = HASH_MAX_LEN;
                            break;
                        case "zset":
                            len = jedis.zcard(key);
                            maxLen = HASH_MAX_LEN;
                            break;
                        default:
                            break;
                    }
                    if (len >= maxLen) {
                        System.out.printf("大Key的值 : %s, type: %s, length or size: %d %n", key, type, len);
                    }
                    // 检查是否已遍历完所有元素
                }
            }
                while (!"0".equals(cursor));
        }
    }
    
  3. 第三方工具

  4. 网络监控

如何删除大BigKey

bigkey内存占用较多,即使删除也需要占用非常多的时间,导致redis主线程阻塞

  • redis3.0以下版本:如果是集合类型则遍历bigkey的元素,逐个删除.建议使用scan扫描然后以此删除
  • redis4.0以后:提供了异步删除命令:unlink

3.使用恰当的数据类型

在这里插入图片描述

在这里插入图片描述

/**
     * 给大key瘦身 拆分成小hash
     */
    @Test
    void smallHash(){
        Jedis jedis = new Jedis("localhost", 6379);

        int hashSize = 100;
        Map<String, String> map = new HashMap<>(hashSize);
        for (int i = 0; i < 100000; i++) {
            int k = (i -1) / hashSize;
            int v = i % hashSize;
            map.put("key_" + v , "value_" + v);
            if (v == 0){
                jedis.hmset("test:small:hash_" + k, map);
            }
        }
    }