资讯动态

Redis 内存涨到 80%:Spring Boot 缓存漏设 TTL 的排查与修复

发布时间:2026/10/23 9:16:40 来源:尧图企业网站定制
本文摘要Spring 缓存未设 TTLRedis 键永不过期、内存持续上涨。从内存采样逆向定位到缺省配置按缓存名注入差异化 TTL 完成修复。方案仅对新键生效存量无 TTL 键需单独清理。一、问题与结论凌晨告警触发Redisused_memory达到maxmemory的 80%但全部业务接口响应正常数据库未见异常负载。登录实例执行INFO memory关键字段如下典型场景还原未实际运行used_memory_human:8.00G maxmemory_human:10.00G maxmemory_policy:noeviction expired_keys:0 evicted_keys:0expired_keys:0是最刺眼的信号——自进程启动以来没有任何键自然过期。与此同时业务侧反馈缓存命中率在下降、回源频率上升虽然没有报错但数据库连接池压力持续攀升。结论根因是RedisCacheManager使用了RedisCacheConfiguration.defaultCacheConfig()其entryTtl默认不设置过期时间导致Cacheable写入的每个键在 Redis 中永久驻留。这不是罕见写法——多数项目起步时直接写RedisCacheManager.builder(factory).build()上线后再无回顾。内存被历史键占满同时应用层或运维脚本对部分热键做了手动删除造成内存高 频繁回源的矛盾表象。二、排查与选择依据排查从 Redis 服务端逆向到应用端而非从代码正向猜测。第一步确认键无 TTL。redis-cli SCAN0MATCH users::* COUNT1000redis-cli TTL users::1# 预期输出(integer) -1 键存在且永不过期redis-cli MEMORY USAGE users::1# 预期输出(integer) 192 单个键占用约 192 字节第二步统计键空间规模定位大 key。redis-cli--bigkeys# 输出各数据类型的键数量与最大值字节数如果某个缓存键单个就占了上百 KB说明业务将整张表或列表序列化后一次性写入这会放大无 TTL 造成的内存膨胀。第三步回查 Spring 配置。搜索application.yml和所有Configuration类若没有出现entryTtl或time-to-live即可锁定根因。第四步确认淘汰策略。redis-cli CONFIG GET maxmemory-policy# 预期输出1) maxmemory-policy 2) noevictionnoeviction在内存满时直接拒绝写入。更隐蔽的坑是volatile-*系列——它们只淘汰有 TTL 的键当所有键都无 TTL 时这些策略完全失效。替代方案与取舍方案适用条件代价边界编程式entryTtl本文方案缓存策略可统一可接受重启窗口需改配置类并重启存量键仍无 TTL只影响新写入老键需另行处理spring.cache.redis.time-to-live属性只需全局统一 TTL不想写 Java 代码无法按缓存名差异化适合单缓存或策略统一的项目直接用StringRedisTemplate缓存逻辑复杂需精细控制序列化与穿透放弃声明式注解代码量增加适合缓存策略稳定、要求高可控性的系统maxmemory-policy allkeys-lru兜底应急止血来不及改代码淘汰热数据导致命中率抖动不是根治达到maxmemory后才触发什么时候不该用本文方案如果 Redis 同时承担 Session、分布式锁等非缓存职责全局淘汰策略会误伤这些键。此时应确保缓存键有 TTL 而非依赖allkeys-lru非缓存键需单独设计生命周期。如果键空间已非常大且含大 key仅补 TTL 不能立刻降内存需同步排查大 key 的写入路径。三、关键原理TTL 是写入时属性。SET key value EX 300中的EX 300在写入瞬间决定过期时间。修改 Spring 配置后新写入的键带 TTL但重启前已写入的键不会自动获得过期时间——这是修复后内存仍然不降的直接原因。maxmemory-policy的触发时机。Redis 仅在used_memory maxmemory时才执行淘汰。在此之前无论noeviction还是allkeys-lru都不会主动回收任何键。没有 TTL 的键在volatile-*系列策略下永远不会被选中。Spring 缓存的缺省传递链Cacheable → RedisCacheManager → RedisCacheConfiguration.defaultCacheConfig() → entryTtl 不设置 → Redis 不带 EX 参数写入 → 键永不过期四、可运行示例环境Spring Boot 3.2.x Spring Data Redis 3.2.x Redis 7.x Java 17。依赖pom.xmldependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency问题配置不设 TTLConfigurationEnableCachingpublicclassCacheConfig{BeanpublicRedisCacheManagercacheManager(RedisConnectionFactoryfactory){returnRedisCacheManager.builder(factory).build();// 未配置 defaults()使用 defaultCacheConfig() 缺省值}}业务代码ServicepublicclassUserService{Cacheable(users)publicUserfindById(Longid){returnqueryDatabase(id);}}调用接口写入缓存后执行redis-cli TTL users::1预期输出(integer) -1键存在永不过期。此行为取决于 Spring Data Redis 版本的缺省值3.x 中defaultCacheConfig()的entryTtl为不设置过期未验证。修复配置按缓存名差异化 TTLConfigurationEnableCachingpublicclassCacheConfig{Value(${app.cache.ttl:30s})privateDurationdefaultTtl;BeanpublicRedisCacheManagercacheManager(RedisConnectionFactoryfactory){RedisCacheConfigurationdefaultsRedisCacheConfiguration.defaultCacheConfig().entryTtl(defaultTtl);MapString,RedisCacheConfigurationoverridesnewHashMap();overrides.put(users,defaults.entryTtl(Duration.ofMinutes(5)));overrides.put(hotConfigs,defaults.entryTtl(Duration.ofSeconds(30)));returnRedisCacheManager.builder(factory).cacheDefaults(defaults).withInitialCacheConfigurations(overrides).build();}}实际输出修复后重新调用接口写入users::1后再次执行redis-cli TTL users::1# (integer) 300常见失败改了配置但内存不降。原因是 TTL 只在写入时生效重启前的存量键仍为-1。修复方式是在低峰期为存量键补过期时间redis-cli--scan--patternusers::*|\xargs-I{}redis-cli EXPIRE{}300或用SCAN配合脚本分批处理避免KEYS在大键空间上阻塞。五、验证结果与边界修复前后的INFO memory对比预期场景还原未实际运行# 修复前无 TTL 持续累积 used_memory_human:8.00G expired_keys:0 # 修复 存量清理后运行数小时 used_memory_human:1.2G expired_keys:183247expired_keys从 0 开始持续增长说明 TTL 真正生效used_memory下降则说明存量清理到位。适用边界该方案控制的是键数量的增长上界。单个大 key如缓存了整张列表需另用MEMORY USAGE或--bigkeys定位并拆分。如果 Redis 同时承担 Session、消息队列等非缓存职责缓存 TTL 无法覆盖这些键需按用途分别设计生命周期。spring.cache.redis.time-to-live属性在 Spring Boot 3.x 中仍然存在但只能设全局统一值无法按缓存名差异化本文优先采用编程式配置。Spring Boot 1.x 不支持RedisCacheConfigurationbuilder 模式需改用构造函数方式单独配置 TTL。思考Redis 同时承担缓存与 Session 时全局allkeys-lru淘汰和按用途拆分实例哪种运维成本更可控存量无 TTL 键数百万时分批EXPIRE补过期与FLUSHDB重建缓存停机窗口和数据损失如何取舍参考资料

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑