Spring分页请求缓存及数据清除问题咨询
这个问题我之前也碰到过,Spring Cache 默认的 @CacheEvict 确实没法直接按前缀清除一批缓存条目,不过有几种靠谱的解决办法,看你用的缓存实现和需求来选:
1. 如果你用的是支持模糊匹配的缓存(比如 Redis)
这是最省心的方案,直接在修改用户数据的方法上用 @CacheEvict,指定匹配该用户所有缓存条目的 key 模式就行:
@CacheEvict(value = "foo", key = "#userId + '*'") public void updateUserData(String userId, UserUpdateRequest request) { // 你的用户数据修改逻辑 }
不过要注意,得确保你的缓存配置支持模糊删除。比如用 Redis 的话,需要在 CacheManager 配置里开启 key 的前缀(默认一般是开启的),Redis 本身支持 SCAN 命令匹配 key,Spring 的 RedisCache 会自动处理这种模糊删除逻辑。
2. 通用方案:手动通过 CacheManager 清除指定用户的缓存
如果你的缓存实现不支持模糊匹配(比如默认的 ConcurrentMapCache),可以手动注入 CacheManager,然后遍历缓存条目删除符合条件的:
@Autowired private CacheManager cacheManager; public void updateUserData(String userId, UserUpdateRequest request) { // 先执行用户数据修改逻辑 updateUserInDb(userId, request); // 清除该用户的所有分页缓存 Cache cache = cacheManager.getCache("foo"); if (cache != null) { if (cache instanceof ConcurrentMapCache) { // 针对内存缓存的处理 ConcurrentMap<Object, Object> nativeCache = ((ConcurrentMapCache) cache).getNativeCache(); Iterator<Map.Entry<Object, Object>> iterator = nativeCache.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Object, Object> entry = iterator.next(); Object cacheKey = entry.getKey(); // 你的缓存 key 是 {userId, page, size} 的数组,取第一个元素判断 if (cacheKey instanceof Object[] && userId.equals(((Object[]) cacheKey)[0])) { iterator.remove(); } } } else { // 如果是其他缓存(比如 Caffeine),可以用对应的 API 处理 // 比如 Caffeine 可以通过 asMap() 获取缓存的 Map 视图,再遍历删除 } } }
这种方法的好处是适配所有缓存实现,缺点是需要自己写遍历逻辑,缓存条目多时可能有轻微性能开销。
3. 优化缓存 key 设计,简化清除逻辑
如果你能接受调整缓存粒度,可以把同一个用户的所有分页缓存放到一个条目里:用 userId 作为缓存 key,缓存值是一个 Map,key 是 page_size 的组合,value 是对应的 Bar 对象:
@Autowired private CacheManager cacheManager; @Cacheable(value = "foo", key = "#userId") public Map<String, Bar> getPaginatedResponse(String userId, int page, int size) { String pageSizeKey = page + "_" + size; Cache cache = cacheManager.getCache("foo"); Map<String, Bar> userPageCache = cache.get(userId, Map.class); // 缓存存在且包含目标分页数据,直接返回 if (userPageCache != null && userPageCache.containsKey(pageSizeKey)) { return userPageCache; } // 查询数据库获取目标分页数据 Bar bar = fetchBarFromDb(userId, page, size); // 初始化或更新缓存的 Map if (userPageCache == null) { userPageCache = new HashMap<>(); } userPageCache.put(pageSizeKey, bar); cache.put(userId, userPageCache); return userPageCache; }
然后修改用户数据时,只需要清除该 userId 对应的单个缓存条目:
@CacheEvict(value = "foo", key = "#userId") public void updateUserData(String userId, UserUpdateRequest request) { // 修改逻辑 }
这种方法的优点是清除逻辑超级简单,缺点是缓存粒度变大——只要用户任意一页数据更新,整个用户的分页缓存都会失效,适合分页数据更新不频繁的场景。
内容的提问来源于stack exchange,提问作者o.m.
相关产品推荐
相关产品推荐

