不能直接用 delete 清理前缀缓存,因为 redistemplate.delete() 仅支持精确匹配单个 key;而业务中缓存 key 带统一前缀(如"user:1001"),无法预知全部 key,硬编码或查库删除既不可靠又破坏解耦;scan 是唯一可行的非阻塞批量匹配方案。

为什么不能直接用 delete 清理前缀缓存
因为 RedisTemplate.delete(String key) 只支持精确匹配单个 key,而实际业务中缓存 key 往往带统一前缀(如 "user:1001"、"user:1002"),你不可能事先知道所有 key 名。硬编码遍历或查数据库再删,既不可靠又破坏缓存解耦原则。
scan 是唯一可行的批量匹配方案
Redis 原生命令 SCAN 支持游标式模糊匹配,不会阻塞服务,是清理前缀缓存的底层基础。Spring Data Redis 通过 RedisCallback 暴露原生连接,让你能安全调用它。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
RedisTemplate.execute(RedisCallback)提供对RedisConnection的直接访问 - 必须用
scan而非keys:后者在大数据量下会阻塞 Redis,生产禁用 - pattern 写法注意:通配符是
*,不是正则;前缀"user:*"合法,"user:\d+"不合法 - 每次
scan返回一批结果(默认 10),需循环直到游标返回0
public void deleteKeysByPattern(String pattern) {
redisTemplate.execute((RedisCallback<long>) connection -> {
ScanOptions options = ScanOptions.scanOptions()
.match(pattern)
.count(100) // 每次扫描数量,建议 100–500
.build();
Cursor<byte> cursor = connection.scan(options);
long deleted = 0;
while (cursor.hasNext()) {
byte[] key = cursor.next();
connection.del(key);
deleted++;
}
cursor.close();
return deleted;
});
}</byte></long>
使用 @CacheEvict 无法替代 scan 清前缀
即使你用了 @CacheEvict(cacheNames = "userCache", allEntries = true),它也只清该 cacheName 下由 Spring Cache 管理的 key —— 这些 key 必须是通过 @Cacheable/@CachePut 写入的,且依赖内部维护的 sorted set(如 spring:cache:names:userCache)。手写 RedisTemplate.opsForValue().set("user:1001", ...) 的 key 完全不在它的管理范围内。
- 自定义写入的 key(如 token、session、临时数据)必须走
scan + delete -
allEntries = true本质是读取并删除一个元数据集合,不是真扫描 key 空间 - 若没配
RedisCacheManager或禁用了元数据跟踪(usePrefix = false),allEntries甚至不生效
容易被忽略的三个细节
真正上线时,这几个点常导致清不干净或超时:
- pattern 中的冒号
:要转义吗?不需要 ——SCAN本身不解析冒号,"user:*"就是合法 pattern - 连接未释放:务必调用
cursor.close(),否则连接泄露,尤其在高并发触发清理时 - 事务干扰:如果在
RedisCallback内执行了multi(),scan会报错(SCAN 不支持事务上下文)










