当前位置: 首页 > news >正文

redis 大key问题整理

整理网上内容,未实践过,如有错误,以你为准

什么是大key问题

Redis中的大key问题是指当单个key对应的value数据量过大时,会引发的一系列性能和运维问题。
大key通常指:
字符串类型:值大小超过10KB
集合类型:元素数量超过5000个
整体大小:超过10KB的任意类型key

大key带来的问题

  1. 性能阻塞‌
    Redis采用单线程模型,处理大key会长时间占用CPU
    阻塞后续命令执行,导致整体响应延迟升高
    例如删除百万元素的Hash可能耗时数秒
  2. 内存管理问题‌
    大key占用连续内存,加剧内存碎片化
    删除大key时内存释放耗时,影响内存回收
    增加内存溢出风险,可能触发数据驱逐
  3. 网络拥塞‌
    查询大key产生大量网络传输
    消耗服务器带宽,增加客户端等待时间
  4. 主从复制与集群问题‌
    主从同步时,大key修改会引发复制风暴
    消耗主节点带宽,增加复制延迟
    Redis Cluster中迁移大key困难,可能导致迁移失败
  5. 持久化问题‌
    生成RDB或重写AOF时序列化大key耗时
    延长BGSAVE执行时间,增加fork阻塞风险
    备份文件体积膨胀,恢复时间更长

大key如何产生的

  1. 设计阶段考虑不足‌

    • 数据结构选择不当:‌错误地用单个 Key 存储过大的数据集(如:将百万级用户 ID 全塞进一个 Set 中)。
    • 未预估数据增长(如:一个存储文章评论的 List,随时间暴增至数十万条)。
    • 未做分片拆分:‌将本应拆分存储的数据集中到一个 Key(如:用单个 Hash 存储所有用户的配置信息)。
  2. 业务场景特性‌

    • 高频写入聚合类 Key:‌实时计数器(如:全网点击量统计 Key global:clicks)持续递增,值变得巨大。排行榜场景中,超大有序集合(ZSET)存储全量用户积分(如:leaderboard:total 含千万成员)。
    • 缓存大对象:‌
      将整页 HTML 结果、大型 JSON 或序列化对象(如图片/文件二进制)存入 String 类型 Key。
    • 消息堆积:‌List 或 Stream 作为消息队列时,若消费者故障,未处理消息堆积成超大 Key。
  3. 技术实现问题‌

    • 未设置过期时间(TTL):‌临时缓存数据或会话信息未设 TTL,长期累积成无用大 Key。
    • 批量操作未拆分:‌一次性从数据库加载海量数据到 Redis(如:HMSET user:data … 导入 10 万字段)。
    • Lua 脚本生成大 Value:‌
      脚本中聚合处理数据后返回超大结果(如:统计全库订单并返回明细)。
    • 删除操作遗漏:‌
      业务删除逻辑有 Bug,导致部分数据残留(如:用户注销后关联数据未清除)。

经典案例场景‌
数据类型‌ ‌典型大 Key 产生场景‌ ‌示例‌
String‌ 缓存大文件/图片 file:pdf:1001 (存储 20MB PDF)
Hash‌ 存储百万字段的对象 product:detail:cache (所有商品属性)
List‌ 未消费的消息堆积 mq:order:pay (堆积 50 万条消息)
Set‌ 全量用户标签/好友关系 users:all_friends (千万级用户ID)
ZSet‌ 大型排行榜/延迟队列 rank:global (含 200 万成员)
Stream‌ 未ACK的流数据累积 log:tracking (未处理日志堆积)

如何排查大key问题

  1. 使用redis内置命令

    • 使用–bigkeys参数
      redis提供了bigkeys参数来查找大key:

      redis-cli --bigkeys
      

      该命令会扫描Redis中的所有key,并返回每种数据类型中最大的key。为了减少对线上服务的影响,可以使用-i参数控制扫描频率:

      redis-cli --bigkeys -i 3
      

      这表示每次扫描后休息3秒,降低对redis的影响。

    • 使用scan命令结合数据结构命令
      可以使用SCAN命令按照一定模式和数量返回匹配的key,然后使用相应的命令获取key的大小:

      数据结构命令复杂度结果
      StringSTRLENO(1)字符串值的长度
      HashHLENO(1)哈希表中字段的数量
      ListLLENO(1)列表元素数量
      SetSCARDO(1)集合元素数量
      Sorted SetZCARDO(1)有序集合的元素数量

      对于Redis 4.0及以上版本,还可以使用MEMORY USAGE命令直接查看key占用的内存大小:

      MEMORY USAGE key_name
      
  2. 分析RDB文件
    通过分析Redis的RDB持久化文件,可以离线找出大key,不会对线上服务产生影响。

    • 使用redis-rdb-tools
      redis-rdb-tools是一个Python编写的工具,可以分析Redis的RDB文件:

      pip install rdbtools python-lzf
      rdb -c memory dump.rdb > memory.csv
      

      生成的CSV文件包含每个key的内存占用情况。

    • 使用rdb_bigkeys
      rdb_bigkeys是Go语言编写的工具,性能更好:

      ./rdb_bigkeys -f dump.rdb -t 10
      

这将列出占用内存前10的key。

  1. 使用云服务提供的工具
    如果使用的是云服务提供商的Redis服务,通常会提供大key分析功能:
  • 阿里云Redis:提供实时Top Key统计和离线全量Key分析功能
  • 腾讯云Redis:提供大key分析和热key分析功能
  • AWS ElastiCache:提供CloudWatch监控和Redis INFO命令集成

Redis大key解决方法

  1. 拆分大key
  • 按业务维度拆分
    将一个大key拆分为多个小key,每个小key存储部分数据:

    • Hash拆分:将一个大Hash拆分为多个小Hash,可以按照二次哈希或业务属性拆分

    • List拆分:将一个大List拆分为多个小List,可以按照时间范围或数据特征拆分

    • Set/Sorted Set拆分:按照某种规则将集合元素分散到多个key中
      示例代码(Hash拆分):

      // 原来的方式
      hset user:1001 field1 value1 field2 value2 ...// 拆分后的方式
      hset user:1001:profile name value age value ...
      hset user:1001:preferences theme value language value ...
  • 使用分片技术
    对于需要作为整体使用的大key,可以使用客户端分片技术:

    // 计算分片
    int shardNumber = key.hashCode() % TOTAL_SHARDS;
    String shardKey = originalKey + ":" + shardNumber;// 写入数据
    jedis.hset(shardKey, field, value);// 读取数据(需要遍历所有分片)
    for (int i = 0; i < TOTAL_SHARDS; i++) {String shardKey = originalKey + ":" + i;Map<String, String> result = jedis.hgetAll(shardKey);// 处理结果
    }
  1. 压缩数据
    对于String类型的大key,可以考虑使用压缩算法减小数据体积:

    // 压缩数据
    byte[] originalData = getOriginalData();
    byte[] compressedData = compress(originalData); // 使用GZIP、Snappy等压缩算法
    jedis.set(key, compressedData);// 读取并解压
    byte[] compressedData = jedis.get(key);
    byte[] originalData = decompress(compressedData);

    需要注意的是,压缩和解压缩会消耗额外的CPU资源,需要在内存占用和CPU消耗之间做权衡。

  2. 使用合适的数据结构
    根据实际需求选择合适的数据结构,避免不必要的内存占用:

    • 使用BitMap替代Set存储布尔类型数据(如用户签到记录)
    • 使用HyperLogLog替代Set进行基数统计(如UV统计)
    • 使用Sorted Set的score特性替代复杂的数据结构
  3. 设置过期时间和惰性删除

  • 设置合理的过期时间
    为大key设置合理的过期时间,让Redis自动清理:

    EXPIRE key_name 3600  # 设置1小时过期
    
  • 使用惰性删除
    对于Redis 4.0及以上版本,可以使用UNLINK命令代替DEL命令,异步删除大key:

    UNLINK key_name
    

    对于Redis 4.0以下版本,可以使用Lua脚本分批删除大key:

    -- 分批删除Hash
    local cursor = 0
    local count = 0
    repeatlocal result = redis.call('HSCAN', KEYS[1], cursor, 'COUNT', 100)cursor = tonumber(result[1])local elements = result[2]if (#elements > 0) thenfor i = 1, #elements, 2 doredis.call('HDEL', KEYS[1], elements[i])count = count + 1endend
    until cursor == 0
    return count
    
  1. 使用其他存储方案
    对于不适合存储在Redis中的大数据,考虑使用其他存储方案:
    • 将大文件存储在对象存储(如OSS、S3)中,Redis只存储文件路径
    • 使用时序数据库存储大量时间序列数据
    • 使用搜索引擎存储需要全文检索的数据

预防Redis大key的最佳实践

  1. 设计阶段预防
    预估数据规模:在设计阶段充分评估数据量的增长趋势
    合理设计key结构:避免将过多数据集中在单个key中
    选择合适的数据结构:根据业务需求选择最合适的Redis数据类型
  2. 开发阶段预防
    代码审查:在代码审查中关注Redis操作,避免产生大key
    单元测试:编写测试用例验证Redis操作的内存占用
    性能测试:在上线前进行压力测试,评估Redis的性能表现
  3. 运维阶段预防
    监控告警:设置大key监控和告警机制,及时发现问题
    定期检查:定期执行大key扫描,主动发现潜在问题
    容量规划:根据业务增长情况,提前规划Redis集群容量

参考:
https://blog.csdn.net/weixin_56018532/article/details/148401783

http://www.dtcms.com/a/578887.html

相关文章:

  • 如何解决 pip install --target 安装成功但脚本不在 PATH(无法调用)问题
  • 深圳企业集团网站建设一个人制作网站
  • 网站外链建设原则中国最大建筑招聘网
  • 李沐深度学习笔记D2-矩阵计算
  • 做期货关注网站淄博网站排名优化报价
  • 创新的南昌网站建设住房和城乡建设部网站城市稽查
  • 张家口建设局网站网站规划的内容
  • 花生壳做网站速度古典asp网站源码
  • leetcode 2208 将数组和减半的最少操作次数
  • 免费的外贸网站怎么搭建个人网站电脑做服务器
  • php网站开发软件是什么烟台芝罘区住房建设局网站
  • AI代码编辑器 - AI代码框架 - AIDevFlow - 使用手册
  • 镇江网站建设哪家好阳江市企业网站优化
  • 教程网站后台密码杭州网站建设unohacha
  • 系统运维Day01_SSH服务
  • 长沙企业网站建设优度wordpress英文
  • 一二三线协同 | IT运维系统如何实现问题处理的高效闭环
  • 建设银行淮安招聘网站阿里云网站建设官方自营店
  • 做外贸在那些网站找业务增城网站开发
  • 【HarmonyOS-App发布】
  • 建网站的费用是多少钱旅游电子商务网站建设的流程
  • JVM(Java Virtual Machine)
  • css全局样式初始化
  • 产品类网站手机怎么下载网页上的视频
  • linux一次性批量更新文件时间戳
  • 阿里云服务器可以做下载类网站吗整容网站模板
  • 萤石开放平台控制台视频直播(原播放地址)操作指南
  • 【stm32简单外设篇】- HC-SR501 / 人体红外被动红外传感器
  • 电子商务网站开发的关键点沈阳建设工程交易网官网
  • 无锡网站建设哪家公司好做网站和app多少费用