redis 大key问题整理
整理网上内容,未实践过,如有错误,以你为准
什么是大key问题
Redis中的大key问题是指当单个key对应的value数据量过大时,会引发的一系列性能和运维问题。
大key通常指:
字符串类型:值大小超过10KB
集合类型:元素数量超过5000个
整体大小:超过10KB的任意类型key
大key带来的问题
- 性能阻塞
Redis采用单线程模型,处理大key会长时间占用CPU
阻塞后续命令执行,导致整体响应延迟升高
例如删除百万元素的Hash可能耗时数秒
- 内存管理问题
大key占用连续内存,加剧内存碎片化
删除大key时内存释放耗时,影响内存回收
增加内存溢出风险,可能触发数据驱逐
- 网络拥塞
查询大key产生大量网络传输
消耗服务器带宽,增加客户端等待时间
- 主从复制与集群问题
主从同步时,大key修改会引发复制风暴
消耗主节点带宽,增加复制延迟
Redis Cluster中迁移大key困难,可能导致迁移失败
- 持久化问题
生成RDB或重写AOF时序列化大key耗时
延长BGSAVE执行时间,增加fork阻塞风险
备份文件体积膨胀,恢复时间更长
大key如何产生的
-
设计阶段考虑不足
- 数据结构选择不当:错误地用单个 Key 存储过大的数据集(如:将百万级用户 ID 全塞进一个 Set 中)。
- 未预估数据增长(如:一个存储文章评论的 List,随时间暴增至数十万条)。
- 未做分片拆分:将本应拆分存储的数据集中到一个 Key(如:用单个 Hash 存储所有用户的配置信息)。
-
业务场景特性
- 高频写入聚合类 Key:实时计数器(如:全网点击量统计 Key global:clicks)持续递增,值变得巨大。排行榜场景中,超大有序集合(ZSET)存储全量用户积分(如:leaderboard:total 含千万成员)。
- 缓存大对象:
将整页 HTML 结果、大型 JSON 或序列化对象(如图片/文件二进制)存入 String 类型 Key。 - 消息堆积:List 或 Stream 作为消息队列时,若消费者故障,未处理消息堆积成超大 Key。
-
技术实现问题
- 未设置过期时间(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问题
-
使用redis内置命令
-
使用–bigkeys参数
redis提供了bigkeys参数来查找大key:redis-cli --bigkeys该命令会扫描Redis中的所有key,并返回每种数据类型中最大的key。为了减少对线上服务的影响,可以使用-i参数控制扫描频率:
redis-cli --bigkeys -i 3这表示每次扫描后休息3秒,降低对redis的影响。
-
使用scan命令结合数据结构命令
可以使用SCAN命令按照一定模式和数量返回匹配的key,然后使用相应的命令获取key的大小:数据结构 命令 复杂度 结果 String STRLEN O(1) 字符串值的长度 Hash HLEN O(1) 哈希表中字段的数量 List LLEN O(1) 列表元素数量 Set SCARD O(1) 集合元素数量 Sorted Set ZCARD O(1) 有序集合的元素数量 对于Redis 4.0及以上版本,还可以使用MEMORY USAGE命令直接查看key占用的内存大小:
MEMORY USAGE key_name
-
-
分析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。
- 使用云服务提供的工具
如果使用的是云服务提供商的Redis服务,通常会提供大key分析功能:
- 阿里云Redis:提供实时Top Key统计和离线全量Key分析功能
- 腾讯云Redis:提供大key分析和热key分析功能
- AWS ElastiCache:提供CloudWatch监控和Redis INFO命令集成
Redis大key解决方法
- 拆分大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);// 处理结果 }
-
压缩数据
对于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消耗之间做权衡。
-
使用合适的数据结构
根据实际需求选择合适的数据结构,避免不必要的内存占用:- 使用BitMap替代Set存储布尔类型数据(如用户签到记录)
- 使用HyperLogLog替代Set进行基数统计(如UV统计)
- 使用Sorted Set的score特性替代复杂的数据结构
-
设置过期时间和惰性删除
-
设置合理的过期时间
为大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
- 使用其他存储方案
对于不适合存储在Redis中的大数据,考虑使用其他存储方案:- 将大文件存储在对象存储(如OSS、S3)中,Redis只存储文件路径
- 使用时序数据库存储大量时间序列数据
- 使用搜索引擎存储需要全文检索的数据
预防Redis大key的最佳实践
- 设计阶段预防
预估数据规模:在设计阶段充分评估数据量的增长趋势
合理设计key结构:避免将过多数据集中在单个key中
选择合适的数据结构:根据业务需求选择最合适的Redis数据类型 - 开发阶段预防
代码审查:在代码审查中关注Redis操作,避免产生大key
单元测试:编写测试用例验证Redis操作的内存占用
性能测试:在上线前进行压力测试,评估Redis的性能表现 - 运维阶段预防
监控告警:设置大key监控和告警机制,及时发现问题
定期检查:定期执行大key扫描,主动发现潜在问题
容量规划:根据业务增长情况,提前规划Redis集群容量
参考:
https://blog.csdn.net/weixin_56018532/article/details/148401783
