RedissonSpringCacheManager缓存类型与TTL失效异常问题问询
1. 为何RedissonSpringCacheManager采用Hash类型存储缓存?
RedissonSpringCacheManager默认将同一个cacheName下的所有缓存条目集中存储在单个Redis Hash结构中:Hash的Key对应cacheName,Hash的Field对应缓存方法的参数键(比如你代码中的#id),Hash的Value则是缓存的对象。
这种设计是Redisson默认的存储策略(由CacheConfig.setStoreCacheAsMap(true)控制,默认开启),核心优势是便于统一管理同一缓存命名空间下的所有条目——比如批量删除某个cacheName下的所有缓存时,只需删除这个Hash键即可,无需遍历所有独立Key。
而RedisCacheManager采用独立Key存储,每个缓存条目对应一个单独的Redis Key(格式为cacheName::key),属于分散存储模式,与Redisson的默认策略完全不同。
2. TTL未正常生效的原因及解决方法
原因分析
Redis原生的Hash结构不支持为单个Field设置TTL,Redisson为了实现Hash存储下的条目过期,只能通过后台定时扫描任务检测并删除过期的Hash字段。这种惰性+定期清理的机制必然存在延迟,导致实际过期时间与配置的TTL产生偏差,这就是你看到缓存未准时过期的核心原因。
而RedisCacheManager使用独立Key存储时,直接利用Redis原生的Key过期机制,Redis会主动对过期Key进行精准清理,所以TTL生效更准时。
解决方法
修改CacheConfig配置,关闭Hash存储模式,改用独立Key存储:
CacheConfig testCacheOrg = new CacheConfig(); testCacheOrg.setTTL(60000); testCacheOrg.setStoreCacheAsMap(false); // 关闭Hash存储,切换为独立Key模式 config.put("demo_org_cache", testCacheOrg);
修改后,RedissonSpringCacheManager会将每个缓存条目存储为独立的Redis Key(格式类似demo_org_cache:1),并直接利用Redis原生的Key过期机制,TTL就能和RedisCacheManager一样准时生效。
如果坚持使用Hash存储模式,可以调整Redisson的过期清理任务间隔(通过Redisson配置文件中的cleanUpDelay和cleanUpKeysAmount参数),但仍无法达到Redis原生Key过期的精准度。
修改后的SpringBootApplication类代码
package com.example.demo; import java.util.HashMap; import java.util.Map; import org.redisson.api.RedissonClient; import org.redisson.codec.JsonJacksonCodec; import org.redisson.spring.cache.CacheConfig; import org.redisson.spring.cache.RedissonSpringCacheManager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableCaching public class CahingdemoApplication { public static void main(String[] args) { SpringApplication.run(CahingdemoApplication.class, args); } @Bean public CacheManager cacheManager(RedissonClient redissonClient) { Map<String, CacheConfig> config = new HashMap<>(); CacheConfig testCacheOrg = new CacheConfig(); testCacheOrg.setTTL(60000); testCacheOrg.setStoreCacheAsMap(false); // 新增配置:关闭Hash存储 config.put("demo_org_cache", testCacheOrg); RedissonSpringCacheManager redissonSpringCacheManager = new RedissonSpringCacheManager(redissonClient, config); redissonSpringCacheManager.setCodec(JsonJacksonCodec.INSTANCE); return redissonSpringCacheManager; } }
内容的提问来源于stack exchange,提问作者Vishal Upadhyay

