缓存失效测试必须直连redis验证,而非依赖@cacheevict/@cacheable单元测试;因aop代理在测试中常未生效,且内存map缓存易造成假命中。

缓存失效测试必须绕过Spring Cache抽象层
直接用@CacheEvict或@Cacheable注解跑单元测试,90%的情况测不出真实失效行为——因为Spring AOP代理在测试上下文中可能未生效,或者RedisTemplate底层连接压根没走真实Redis。你看到的“命中/未命中”,很可能是内存Map模拟的假缓存。
真正有效的测试路径只有一条:跳过注解,直连Redis服务,用redisTemplate.delete()或stringRedisTemplate.delete()删键,再用redisTemplate.opsForValue().get()验证是否为空。这样能100%确认缓存层是否真的被清掉。
- 测试前确保
application.yml里spring.redis.host指向真实Redis实例(不是localhost或Docker容器别名) - 禁用
@EnableCaching或临时移除@Cacheable相关方法,避免干扰 - 用
StringRedisTemplate而非RedisTemplate<object></object>,避免序列化差异导致查不到键
验证@CacheEvict是否触发的关键检查点
@CacheEvict不生效,大概率不是代码写错了,而是执行时机或条件没满足。最常踩的坑是beforeInvocation = false(默认值)——这意味着方法抛异常时,缓存根本不会清除。
- 加
beforeInvocation = true,确保无论方法成功或失败都删缓存:@CacheEvict(value = "users", key = "#id", beforeInvocation = true) - 确认
key表达式和实际存入缓存的key完全一致,比如存的是"user:123",但key = "#id"生成的是123,就对不上 - 检查缓存管理器配置:如果定义了多个
CacheManager,value = "users"必须对应其中一个bean的名字,否则静默失败 - 方法不能是private或final——AOP代理无法增强,
@CacheEvict直接被忽略
用Redis CLI手动模拟失效场景更可靠
写一堆JUnit断言不如打开终端敲几行命令。本地开发时,启动应用后立刻连Redis:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
redis-cli -h 192.168.20.150 -p 6379 -a hlc
192.168.20.150:6379> get user:1001
"{"id":1001,"name":"张三"}"
192.168.20.150:6379> del user:1001
(integer) 1
192.168.20.150:6379> get user:1001
(nil)
这时再触发业务接口,观察日志里是否打出数据库查询语句。如果还返回旧数据,说明应用层没走缓存读取逻辑,或者RedisTemplate用了不同database(比如配置里database: 1,但CLI连的是db 0)。
- 注意
redis-cli默认连db 0,而Spring Boot配置的database值必须匹配 - 用
keys *查当前库所有key,确认key命名风格(有无前缀、大小写、JSON转义) - 如果key带
\x00\x00...乱码,说明还在用JdkSerializationRedisSerializer,得换成StringRedisTemplate或配Jackson序列化器
测试缓存击穿要造并发,但别用JUnit自带线程
想验证热点key过期瞬间是否引发击穿?别用Executors.newFixedThreadPool(100)在测试里硬扛——线程调度不可控,且JUnit生命周期短,Redis连接可能提前关闭。
更稳的做法:起一个最小化HTTP服务(比如用Spring Boot Actuator暴露一个/test-cache-break端点),然后用ab或wrk发压:
wrk -t12 -c400 -d10s http://localhost:8080/test-cache-break?id=1001
同时监控MySQL慢查询日志或Druid连接池活跃数。如果同一秒内数据库出现数百次相同SQL,就是击穿实锤。
- 压测前手动设key过期:
EXPIRE user:1001 1,1秒后触发 - 确保被测方法里没有加分布式锁(比如RedissonLock),否则会掩盖击穿问题
- 观察Redis的
INFO commandstats里cmdstat_get和cmdstat_set的调用量差,击穿时get远大于set
@CacheEvict参数有用得多。










