news 2026/4/16 10:55:11

Redis突然变慢,排查发现是BigKey惹的祸

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis突然变慢,排查发现是BigKey惹的祸

线上Redis响应时间从平均1ms飙到了50ms,业务接口全都变慢了。

查了半天,最后发现是一个BigKey导致的。记录一下排查过程。


问题现象

监控数据

  • Redis平均响应时间:1ms → 50ms
  • 业务接口P99延迟:50ms → 500ms
  • Redis CPU:20% → 80%
  • 内存使用:正常

特点

  • 突然变慢,不是逐渐变慢
  • 所有命令都变慢,不只是特定命令
  • 重启后好一段时间,然后又变慢

排查过程

Step 1:查看慢查询日志

redis-cli# 查看慢查询日志SLOWLOG GET20

输出:

1) 1) (integer) 1001 2) (integer) 1702345678 3) (integer) 45123 # 微秒,约45ms 4) 1) "HGETALL" 2) "user:session:12345"

发现大量HGETALL命令耗时几十毫秒,正常应该是亚毫秒级。

Step 2:查看这个Key的信息

# 查看Key类型TYPE user:session:12345# hash# 查看Hash的字段数量HLEN user:session:12345# 182356# 查看Key占用内存DEBUG OBJECT user:session:12345# serializedlength:15728640 约15MB

问题找到了!这个Hash有18万个字段,占用15MB内存。

这就是BigKey,对它执行HGETALL要把18万个字段全部遍历,当然慢。

Step 3:查找其他BigKey

# Redis 4.0+ 可以用 --bigkeys 扫描redis-cli --bigkeys# 或者用 SCAN 配合 DEBUG OBJECTredis-cli --scan --pattern'*'|whilereadkey;dosize=$(redis-cli DEBUG OBJECT"$key"2>/dev/null|grep-oP'serializedlength:\K\d+')if["$size"-gt1048576];then# 大于1MBecho"$key:$sizebytes"fidone

扫描结果发现了多个BigKey:

user:session:12345: 15728640 bytes (15MB) cache:product:list: 8388608 bytes (8MB) temp:import:batch: 5242880 bytes (5MB)

Step 4:分析业务逻辑

查代码发现问题:

// 问题代码:把整个session存成一个大Hash@OverridepublicvoidsaveSession(StringsessionId,Map<String,Object>data){Stringkey="user:session:"+sessionId;// 每次访问都往里加数据,从来不清理redisTemplate.opsForHash().putAll(key,data);}// 获取时用HGETALLpublicMap<String,Object>getSession(StringsessionId){Stringkey="user:session:"+sessionId;returnredisTemplate.opsForHash().entries(key);// HGETALL}

问题

  1. Session数据一直往Hash里加,不删除
  2. 时间一长,Hash就变成了BigKey
  3. 每次获取Session都用HGETALL,遍历整个Hash

BigKey的危害

1. 阻塞单线程

Redis是单线程的,操作BigKey时会阻塞其他命令:

正常Key(1KB): 1ms完成 BigKey(10MB): 50ms完成 这50ms内其他所有命令都在排队等待

2. 网络带宽压力

每次HGETALL返回15MB数据 1秒请求10次 = 150MB/s 网络可能成为瓶颈

3. 内存不均衡

如果是Redis集群,BigKey会导致某个节点内存远大于其他节点。

4. 删除时阻塞

DEL user:session:12345# 删除15MB的Key,可能阻塞好几秒

解决方案

方案一:拆分BigKey

把大Hash拆成多个小Hash:

// 优化前:一个大Hashuser:session:12345{field1:v1,field2:v2,...field180000:v180000}// 优化后:按照某种规则拆分user:session:12345:0{field1:v1,...field1000:v1000}user:session:12345:1{field1001:v1001,...field2000:v2000}...

方案二:改用合适的数据结构

Session数据不需要存18万个字段,只需要保留最近访问的数据:

// 使用String存储序列化后的数据,设置过期时间publicvoidsaveSession(StringsessionId,SessionDatadata){Stringkey="user:session:"+sessionId;Stringjson=JSON.toJSONString(data);redisTemplate.opsForValue().set(key,json,30,TimeUnit.MINUTES);}

方案三:避免HGETALL

// 优化前:获取整个HashMap<String,Object>all=redisTemplate.opsForHash().entries(key);// 优化后:只获取需要的字段Objectvalue=redisTemplate.opsForHash().get(key,"targetField");// 或者批量获取部分字段List<Object>values=redisTemplate.opsForHash().multiGet(key,Arrays.asList("f1","f2"));

方案四:异步删除BigKey

# Redis 4.0+ 支持异步删除UNLINK user:session:12345# 异步删除,不阻塞# 或者渐进式删除Hash# 每次删1000个字段HSCAN user:session:123450COUNT1000HDEL user:session:12345 field1 field2... field1000

最终解决

  1. 临时处理:用UNLINK异步删除那几个BigKey
  2. 代码修复:Session改用String存储,设置30分钟过期
  3. 添加监控:定期扫描BigKey,超过1MB告警

BigKey标准

数据类型BigKey阈值说明
String> 10KB单个值太大
Hash> 5000字段 或 > 10MB字段太多或总大小太大
List> 5000元素元素太多
Set> 5000成员成员太多
ZSet> 5000成员成员太多

排查命令汇总

# 查看慢查询SLOWLOG GET20# 扫描BigKeyredis-cli --bigkeys# 查看Key类型TYPE<key># 查看Hash字段数HLEN<key># 查看List长度LLEN<key># 查看Set成员数SCARD<key># 查看内存占用(需要开启)MEMORY USAGE<key># 查看Key详情DEBUG OBJECT<key># 渐进式扫描HSCAN<key>0COUNT100# 异步删除UNLINK<key>

预防措施

1. 设计阶段

✅ 预估数据量,避免无限增长 ✅ 设置合理的过期时间 ✅ 考虑数据拆分策略

2. 开发阶段

✅ 避免使用HGETALL、SMEMBERS等全量命令 ✅ 大数据量使用SCAN系列命令 ✅ 删除大Key使用UNLINK

3. 运维阶段

✅ 定期扫描BigKey ✅ 监控慢查询 ✅ 设置maxmemory-policy

经验总结

现象可能原因
所有命令都变慢BigKey阻塞
特定命令变慢该命令操作了BigKey
内存突然增长写入了BigKey
主从同步延迟BigKey传输

这次的坑:Session数据只写不删,时间一长变成了18万字段的BigKey。

教训

  1. Redis的Key一定要设置过期时间
  2. 避免使用HGETALL等全量命令
  3. 定期扫描BigKey,加入监控

有问题评论区交流~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 10:55:10

Python EXE解包神器:深度逆向分析PyInstaller和py2exe打包程序

Python EXE解包神器&#xff1a;深度逆向分析PyInstaller和py2exe打包程序 【免费下载链接】python-exe-unpacker 项目地址: https://gitcode.com/gh_mirrors/pyt/python-exe-unpacker 想要透视Python打包的EXE文件内部构造吗&#xff1f;Python EXE解包工具让你轻松掌…

作者头像 李华
网站建设 2026/4/14 10:05:00

高校实验室的数字化转型:RateYourSupervisor实践案例

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个学术机构专用的导师评价系统&#xff0c;需包含&#xff1a;1)院系分级管理 2)多维度评价指标(科研指导、职业发展等) 3)数据可视化仪表盘 4)定时匿名报告生成。特别要求符…

作者头像 李华
网站建设 2026/4/15 18:20:08

LinkSwift:免费网盘直链下载的终极解决方案

LinkSwift&#xff1a;免费网盘直链下载的终极解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 可以获取网盘文件真实下载地址。基于【网盘直链下载助手】修改&#xff08;改自6.1.4版本&#xff09; &#xff0c;自用&#xff0c;去推广&#xff0c;无需…

作者头像 李华
网站建设 2026/4/16 10:50:22

24、Linux 文件系统操作与系统调用详解

Linux 文件系统操作与系统调用详解 1. EXT2 文件系统操作 1.1 显示根 Inode 信息 在 EXT2 文件系统中,编号为 2(从 1 开始计数)的 inode 是根目录 / 的 inode。通过将根 inode 读取到内存中,我们可以显示其各种字段,如模式、用户 ID、组 ID、文件大小、创建时间、硬链…

作者头像 李华
网站建设 2026/4/15 3:42:59

AI助力Vue-Baidu地图开发:自动生成地图组件代码

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请生成一个Vue 3组件&#xff0c;使用vue-baidu-map库实现一个带标记点的百度地图。要求&#xff1a;1.地图中心定位在北京天安门(39.915,116.404)&#xff1b;2.添加3个标记点&…

作者头像 李华