redisTemplate.delete(Collection) 删除的是 key 本身而非 Hash/Set 内容;批量删需先用 SCAN(非 KEYS)匹配 key,再 delete;必须配置 StringRedisSerializer 避免序列化导致 keys() 失效。

redisTemplate.delete(Collection) 不能直接删 Hash 或 Set 的整个结构
很多人误以为 redisTemplate.delete(keys) 能删掉一个 Hash 或 Set 的全部内容,其实它只删 key 本身——比如你调用 redisTemplate.delete("cart:123"),确实会把整个 Hash 结构干掉;但如果你传的是 redisTemplate.delete(Collections.singletonList("cart:123")),效果一样,仍是删 key。关键点在于:Hash/Set 的“数据”不是独立存在的,它们依附于 key。所以「删整个 Hash」本质上就是「删那个 key」。
但问题来了:你想删的是「所有 cart:* 开头的 Hash」,或者「所有 status:set 开头的 Set」,这时候就不能靠单个 delete(),必须先找出这些 key,再批量删。
keys("cart:*") 返回空?十有八九是序列化器没配对
常见错误现象:redisTemplate.keys("cart:*") 返回空集合,但用 redis-cli KEYS cart:* 却能查到一堆 key。
根本原因:默认 RedisTemplate 的 keySerializer 是 JdkSerializationRedisSerializer,它会给每个 key 加上 Java 序列化头(如 \xac\xed\x00\x05),导致 Redis 服务端用字符串模式匹配时根本找不到原始 key。
解决办法只有这一步:显式设置 StringRedisSerializer 为 key 序列化器:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer()); // Hash 的 field 也建议设
template.setValueSerializer(new StringRedisSerializer()); // 值若为字符串也设
return template;
}不改这个,keys("cart:*") 永远为空,后续所有批量操作都白搭。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
生产环境禁用 KEYS,改用 SCAN 分批处理
KEYS 是阻塞命令,Redis 单线程执行时,如果匹配出几百万 key,整个实例就卡住,影响线上请求。Spring Data Redis 的 redisTemplate.keys() 底层就是发 KEYS,所以绝不能在生产环境调用。
替代方案是 SCAN,它非阻塞、可游标分页。但注意:RedisTemplate 没封装 SCAN 的便捷方法,得手写逻辑:
- 用
redisTemplate.executeWithStickyConnection()获取原生RedisConnection - 循环调用
scan(cursor, ScanOptions.scanOptions().match("cart:*").count(1000).build()) - 每批拿到的 key 集合立即传给
redisTemplate.delete()(内部会走 pipeline,性能好) - 游标为 0 时结束
别试图一次 SCAN 出全部 key 再统一删——内存扛不住,尤其当 key 数超百万时。
删 Hash 中部分字段 vs 删整个 Hash,选错代价很大
如果只是想清理购物车里的某几个商品,用 redisTemplate.opsForHash().delete("cart:123", "itemA", "itemB");但如果要清空整个购物车,直接 redisTemplate.delete("cart:123") 更快、更省资源。
容易踩的坑:
- 对同一个 Hash 多次调用
delete(field),不如一次性传多个 field,减少网络往返 - 误用
opsForHash().values()或entries()先全量读出来再删——纯属浪费带宽和内存 - Set 类型同理:
redisTemplate.opsForSet().remove("user:tags", "temp", "draft")是删元素;redisTemplate.delete("user:tags")才是删整个 Set
真正海量数据场景下,“删 key”永远比“删内部成员”更轻量,只要业务语义允许,优先走 key 级删除。

















