首页 > 数据库 >Redis键值设计与BigKey处理方案

Redis键值设计与BigKey处理方案

时间:2024-01-05 12:34:18浏览次数:44  
标签:redis Redis BigKey jedis key import 键值

1. 优雅的key结构

Redis的Key虽然可以自定义,但最好遵循下面的几个最佳实践约定:

  • 遵循基本格式:[业务名称]:[数据名]:[数据/数据id]
  • 长度不超过44字节
  • 不包含特殊字符

例如:我们的登录业务,保存用户信息,其key可以设计成如下格式:

Redis键值设计与BigKey处理方案_键值设计

优点:

  • 可读性强
  • 避免key冲突
  • 方便管理
  • 更节省内存: key是string类型,底层编码包含int、embstr和raw三种embstr在小于44字节使用,采用连续内存空间,内存占用更小。当字节数大于44字节时,会转为raw模式存储,在raw模式下,内存空间不是连续的,而是采用一个指针指向了另外一段内存空间,在这段空间里存储SDS内容,这样空间不连续,访问的时候性能也就会收到影响,还有可能产生内存碎片

Redis键值设计与BigKey处理方案_键值设计_02

2. BigKey慎用

BigKey通常以Key的大小和Key中成员的数量来综合判定,例如:

  • Key本身的数据量过大:一个String类型的Key,它的值为5 MB
  • Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10,000个
  • Key中成员的数据量过大:一个Hash类型的Key,它的成员数量虽然只有1,000个但这些成员的Value(值)总大小为100 MB

那么如何判断元素的大小呢?redis也给我们提供了命令

Redis键值设计与BigKey处理方案_键值设计_03

注意:

  • 一般不推荐使用memory指令,因为memory指令对cpi使用率比较高
  • 实际开发我们一般只需要判断值或者值的个数即可

推荐值:

  • 单个key的value小于10KB
  • 对于集合类型的key,建议元素数量小于1000,个数一般不超过5000

请注意,MEMORY 命令在 Redis 4.0 版本及更高版本中引入。如果您使用的是较旧的版本,您可能需要升级 Redis 到最新版本才能使用 MEMORY 命令

2.1. BigKey的危害

  • 网络阻塞
  • 对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,导致Redis实例,乃至所在物理机变慢
  • 数据倾斜
  • BigKey所在的Redis实例内存使用率远超其他实例,无法使数据分片的内存资源达到均衡

  • Redis阻塞

  • 对元素较多的hash、list、zset等做运算会耗时较旧,使主线程被阻塞

  • CPU压力

  • 对BigKey的数据序列化和反序列化会导致CPU的使用率飙升,影响Redis实例和本机其它应用

3. 如何发现BigKey

3.1.  redis-cli --bigkeys

Redis键值设计与BigKey处理方案_数据_04

利用redis-cli提供的--bigkeys参数,可以遍历分析所有key,并返回Key的整体统计信息与每个数据的Top1的big key

命令:

`redis-cli -a 密码 --bigkeys`

3.2.  scan扫描

自己编程,利用scan扫描Redis中的所有key,利用strlen、hlen等命令判断key的长度(此处不建议使用MEMORY USAGE

Redis键值设计与BigKey处理方案_数据_05

scan 命令调用完后每次会返回2个元素,第一个是下一次迭代的光标,第一次光标会设置为0,当最后一次scan 返回的光标等于0时,表示整个scan遍历结束了,第二个返回的是List,一个匹配的key的数组

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ScanResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JedisTest2 {
    private Jedis jedis;


    @BeforeEach
    void setUp() {
        // 1.建立连接
         jedis = new Jedis("192.168.150.102", 6379);
        // 2.设置密码
//        jedis.auth("123321");
        // 3.选择库
        jedis.select(0);
    }

  
    final static int STR_MAX_LEN = 10 * 1024;
    final static int HASH_MAX_LEN = 500;

    @Test
    void testScan() {
        int maxLen = 0;
        long len = 0;

        String cursor = "0";
        do {
            // 扫描并获取一部分key
            ScanResult<String> result = jedis.scan(cursor);
            // 记录cursor
            cursor = result.getCursor();
            List<String> list = result.getResult();
            if (list == null || list.isEmpty()) {
                break;
            }
            // 遍历
            for (String key : list) {
                // 判断key的类型
                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("发现 big key : %s, type: %s, length or size: %d %n", key, type, len);
                }
            }
        } while (!cursor.equals("0"));
    }

  
    @AfterEach
    void tearDown() {
        if (jedis != null) {
            jedis.close();
        }
    }
}

3.3. 第三方工具

如何删除BigKey

BigKey内存占用较多,即便时删除这样的key也需要耗费很长时间,导致Redis主线程阻塞,引发一系列问题。redis 3.0 及以下版本

如果是集合类型,则遍历BigKey的元素,先逐个删除子元素,最后删除BigKey

  • 使用 DEL 命令:

尽管 DEL 命令是同步的,可能会对性能产生一些影响,但它仍然是删除键的一种方法。你可以使用以下命令:

DEL key

其中,key 是你要删除的键的名称。这个命令会阻塞 Redis 服务器,直到删除操作完成

  • unlink 命令

Redis在4.0后提供了异步删除的命令:unlink

标签:redis,Redis,BigKey,jedis,key,import,键值
From: https://blog.51cto.com/maguobin/9090493

相关文章

  • 无涯教程-Redis - HyperLogLog
    RedisHyperLogLog是一种使用随机算法的算法,目的是仅使用一个常量和少量的内存就可以估算一组集合中唯一元素的数量。HyperLogLog可以很好地近似集合的基数,即使使用很少的内存,标准误差为0.81%,可以计数的项目数没有限制,除非您处理264个项目。HyperLogLog-示例以下示例说明......
  • 无涯教程-Redis - Sorted Sets(排序集)
    RedisSortedSets与RedisSets类似,它具有存储在集合中的值的独特功能,不同之处在于,排序集的每个元素都与一个分数相关联,该分数用于从最小到最大分数中获取排序的排序集。SortedSets-示例redis127.0.0.1:6379>ZADDLearnfk1redis(integer)1redis127.0.0.1:6379>ZA......
  • 无涯教程-Redis - Strings(字符串)
    Redis字符串命令用于管理Redis中的字符串值,以下是使用Redis字符串命令的语法。Strings-语法redis127.0.0.1:6379>COMMANDKEY_NAMEStrings-示例redis127.0.0.1:6379>SETlearnfkredisOKredis127.0.0.1:6379>GETlearnfk"redis"在上面的示例中,SET和GET......
  • 无涯教程-Redis - 命令
    Redis命令用于在Redis服务器上执行一些操作。要在Redis服务器上运行命令,您需要一个Redis客户端,Redis客户端在无涯教程之前安装的Redis软件包中可用。以下是Redis客户端的基本语法。$redis-cli以下示例说明了如何启动Redis客户端。要启动Redis客户端,请打开终端并输入命令re......
  • 无涯教程-Redis - keys(键)
    Rediskeys命令用于管理Redis中的键(key),以下是使用rediskeys命令的语法。Keys-语法redis127.0.0.1:6379>COMMANDKEY_NAMEKeys-示例redis127.0.0.1:6379>SETlearnfkredisOKredis127.0.0.1:6379>DELlearnfk(integer)1在上面的示例中,DEL是命令,而lear......
  • 无涯教程-Redis - 配置文件
    在Redis中,Redis的根目录中有一个配置文件(redis.conf),尽管您可以通过RedisCONFIG命令获取并设置所有Redis配置。GET语法以下是RedisCONFIG命令的基本语法。redis127.0.0.1:6379>CONFIGGETCONFIG_SETTING_NAMEGET示例redis127.0.0.1:6379>CONFIGGETloglevel......
  • 无涯教程-Redis - 简介
    Redis是一个使用ANSIC编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。从2015年6月开始,Redis的开发由RedisLabs赞助,而2013年5月至2015年6月期间,其开发由Pivotal赞助。在2013年5月之前,其开发由VMware赞助。根据月度排行网站DB-Engines.com的数据,Redis是最流行的......
  • redis中序列化问题,value包含全路径类名解析
    1.问题redis中保存的key-value格式value直接存入的是实体对象,值中包含全路径类名,在使用Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer解析器时报错报错内容:com.fasterxml.jackson.databind.exc.InvalidTypeIdException:Couldnotresolvetypeid'entity.r......
  • 程序员的38大Redis面试问题及答案-下
    文章目录1.查看配置语法2.获取所有配置项3.设置字符串4.获取字符串5.获取随机key6.获取key存储的类型7.判断key是否存在8.修改key的名称9.返回key存储的字符串的长度10.同时设置多个kv对11.获取多个key的值12.设置key10秒后过期13.查看当前还剩几秒过期14.过期后获取key15.列表最......
  • 程序员的50大Redis面试问题及答案-上
    文章目录1.Redis是什么?2.Redis特性?3.Redis合适的应用场景?4.除了Redis你还知道哪些NoSQL数据库?5.Redis和Memcache区别?6.Redis的有几种数据类型?7.Redis有哪些高级功能?8.安装过Redis吗,简单说下步骤?9.redis几个比较主要的可执行文件?分别是?10.启动Redis的几种方式?11.Redis配置需要自......