Spring Boot Redis缓存驱逐后校验缓存有效性的实现方法问询
解决方法
为什么getNativeCache没有返回有效内容
Spring Cache是抽象层实现,你通过cacheManager.getCache(cacheName)拿到的Cache实例,在Redis缓存场景下实际类型是RedisCache,它的getNativeCache()返回的是RedisOperations操作对象,不是直接的缓存内容集合,你没有做类型转换就直接调用自然拿不到有效信息。
具体实现步骤
1. 类型转换获取Redis操作实例
先做类型校验与转换,避免强转异常:
import org.springframework.cache.Cache; import org.springframework.data.redis.cache.RedisCache; import org.springframework.data.redis.core.RedisOperations; Cache cache = cacheManager.getCache(cacheName); // 校验缓存实例是否为Redis类型 if (!(cache instanceof RedisCache redisCache)) { // 非Redis缓存实例,直接判定缓存无效,触发对应告警逻辑 throw new IllegalArgumentException("目标缓存非Redis类型"); } // 转换获取Redis操作工具实例 RedisOperations<String, Object> redisOps = (RedisOperations<String, Object>) redisCache.getNativeCache();
2. 校验Redis缓存是否可正常读写
通过写入测试键、读取验证、删除测试键的流程判断缓存可用性:
import java.time.Duration; String testKey = "cache_health_check:" + System.currentTimeMillis(); String testValue = "check_value"; try { // 构造符合当前缓存规则的Redis Key String cacheTestKey = redisCache.createCacheKey(testKey); // 写入测试键,设置10秒过期避免残留 redisOps.opsForValue().set(cacheTestKey, testValue, Duration.ofSeconds(10)); // 读取验证 Object readResult = redisOps.opsForValue().get(cacheTestKey); if (!testValue.equals(readResult)) { // 读写结果不一致,缓存功能异常,触发告警 } // 删除测试键 redisOps.delete(cacheTestKey); } catch (Exception e) { // Redis操作抛出异常,缓存不可用,触发告警 }
3. 统计缓存条目、校验驱逐后是否有残留
Spring RedisCache默认会给当前缓存下所有Key添加[缓存名]::前缀,我们扫描匹配该前缀的Key即可统计条目数量:
注意:生产环境禁止使用
keys命令,大数据量场景下会阻塞Redis,推荐使用scan命令迭代统计
测试环境快速统计(keys命令)
String keyPrefix = cacheName + "::"; Set<String> remainKeys = redisOps.keys(keyPrefix + "*"); int remainSize = remainKeys != null ? remainKeys.size() : 0; if (remainSize > 0) { // 缓存驱逐后仍有残留条目,触发告警 }
生产环境安全统计(scan命令)
import org.springframework.data.redis.core.ScanOptions; import java.util.concurrent.atomic.AtomicInteger; String keyPrefix = cacheName + "::"; ScanOptions scanOpts = ScanOptions.scanOptions() .match(keyPrefix + "*") .count(1000) .build(); AtomicInteger remainSize = new AtomicInteger(0); redisOps.executeWithStickyConnection(conn -> { try (var cursor = conn.scan(scanOpts)) { while (cursor.hasNext()) { cursor.next(); remainSize.incrementAndGet(); } } return null; }); if (remainSize.get() > 0) { // 缓存驱逐后仍有残留条目,触发告警 }
如果是Redis集群部署,需要遍历所有集群节点执行scan操作统计总数。
内容的提问来源于stack exchange,提问作者user2001165
相关产品推荐
相关产品推荐

