接口偶发 5xx 连查 3 天:Redis 连接池耗尽 + 一条慢命令,我把 JedisPool 从 8 调到 200

发布时间:2026/10/27 10:28:49
接口偶发 5xx 连查 3 天:Redis 连接池耗尽 + 一条慢命令,我把 JedisPool 从 8 调到 200 接口偶发 5xx 连查 3 天Redis 连接池耗尽 一条慢命令我把 JedisPool 从 8 调到 200故障发生在零工提现高峰期。线上接口开始偶发 5xx大概 1% 的请求会飘一个 500业务方反馈提现按钮点一下偶尔报错重试就好了。这种偶发问题最磨人——不好复现日志翻半天也抓不到规律。现象与第一直觉先说现象。错误日志里夹杂着这么一条org.springframework.data.redis.RedisSystemException: Unable to fetch a connection from the pool within 3 seconds. JedisConnectionException: Could not get a resource from the pool关键字是Could not get a resource from the pool。第一反应是 Redis 连接池太小但池子默认 8 个连接怎么会被打满带着疑问先做三件事看连接池监控、抓 Redis 慢命令、统计接口耗时分布。排查慢日志 大 key 扫描排查过程一步步来# 1. 先确认 Redis 端有没有慢命令执行超过 100ms 的redis-cli-hSLOWLOG GET20# 2. 看 Redis 内存和 key 分布找大 keyredis-cli--bigkeys--i0.1# 3. 服务端看线程栈确认卡在哪一步jstack|grep-A5JedisSLOWLOG 一查问题立刻现形排行榜接口的ZREVRANGE每次跑 300-600ms慢命令的执行频率还特别高高峰期每秒几十次。慢命令占着连接不放池子里的连接全被慢查询拖住新请求拿不到连接就直接 5xx。根因慢命令占死连接池为什么慢命令会拖垮连接池Redis 是单线程模型一条慢命令执行期间这个连接上的后续命令全在排队。jedis的池子默认 8 个连接如果 3-4 个连接同时被慢命令占着每条 500ms剩下连接很快被抢空max-wait3000ms一到就抛Could not get a resource。慢命令是根连接池小是放大镜。修复一拆分大 key消灭慢命令定位到根因后两条线同时修第一拆分大 key消灭慢命令。排行榜原来是一个 ZSet 存了 60 万成员的job:rank:city:一次ZREVRANGE 0 99要扫大量数据。改成按天分桶 只存 top 500 的近似排行// 原方案全量 ZSet60万成员一次取 top100 很慢Stringkeyjob:rank:city:cityId;redisTemplate.opsForZSet().reverseRange(key,0,99);// 新方案每天一个桶且写时只保留 top 500防冷数据堆积Stringbucketjob:rank:city:cityId:LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);LongtotalredisTemplate.opsForZSet().size(bucket);if(total!nulltotal500){redisTemplate.opsForZSet().removeRange(bucket,500,-1);}ListtopredisTemplate.opsForZSet().reverseRange(bucket,0,99);removeRange(bucket, 500, -1)这步很关键——只保留前 500冷数据永远不堆积ZSet 越小越快。排行榜这种场景根本不需要全量精度近似排行对业务完全够用。改完后ZREVRANGE从 300ms 降到 2ms慢命令消失。第二连接池参数按峰值重配别再裸奔默认值spring:data:redis:jedis:pool:max-active:200# 原来是默认 8提现高峰期根本不够max-idle:50min-idle:10max-wait:5000ms# 等待时间给足别 3 秒就抛异常修复二连接池按峰值重配同时给连接池加了监控埋点活跃连接数、等待数、创建数上报到监控平台后续再出现连接紧张能提前发现。调完后压测5000 并发下连接池活跃数峰值在 60 左右200 的池子留足了余量。连接池参数怎么定给个参考max-active按接口 QPS × 平均每个请求占用 Redis 时长的峰值估别拍脑袋。我们提现接口高峰期 QPS 800平均一次 Redis 操作 2ms理论并发占用就 1.6 个连接——但加上慢命令和批量命令的突发实际活跃峰值到了 60所以 200 的池子既够用又不浪费内存。max-wait 给 5 秒也别太短慢命令修完之前等待时间就是给服务端的缓冲。慢日志阈值和大 key 删除这里有个容易被忽视的细节慢命令不一定每次都慢。我一开始看 SLOWLOG 只有 3 条以为不是问题直到把slowlog-log-slower-than从默认 10000 微秒调到 500 微秒才抓到大量 300ms 的命令。阈值别用默认值线上 Redis 的慢日志阈值建议调到 500 微秒0.5ms否则大多数慢命令根本不会进日志。第三个坑也提一下大 key 删除会阻塞 Redis。排查时发现有个 List key 存了 200 万条历史推送记录用DEL删的时候 Redis 直接卡了 1.2 秒所有请求集体超时。正确姿势# 大 key 别用 DEL用 UNLINK 异步删除redis-cli UNLINK job:push:history:20261001UNLINK是异步释放内存不阻塞主线程线上清理大 key 的标配。这次故障复盘根因一句话排行榜接口的慢命令占死连接池偶发 5xx 是慢查询的次生灾害。修慢命令是治本调连接池是给缓冲两者缺一不可。光调池子不修慢命令过两天数据涨起来照样炸。再补一个容易被忽略的点监控告警要建在慢命令条数上而不是只盯接口 5xx。5xx 是结果慢命令是过程指标等 5xx 冒出来再去查用户已经骂完了。我们上线了每 5 分钟一次的 SLOWLOG 统计告警一旦 500ms 以上命令超过阈值就推送这次故障之后基本做到了慢命令冒头就能发现。过程指标先行故障就不会以最痛的方式来找你。这条经验后来写进了团队的 Redis 运维手册新人照着做就行。可直接复用的排查清单偶发 5xx Could not get a resource from the pool先看慢日志别急着调池子slowlog-log-slower-than 500微秒默认 10000 会漏掉绝大多数慢命令redis-cli --bigkeys扫大 key排行榜 ZSet / 消息 List 是重灾区大 key 删除用UNLINKDEL会阻塞主线程连接池 max-active 按峰值流量给默认 8 是给玩具项目用的上线前压测必须测 Redis 链路慢命令 小池子是 5xx 的经典组合拳项目源码https://gitee.com/gzqkl/xllg

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询