根本原因是RedisTemplate默认用JdkSerializationRedisSerializer序列化key,导致存入的是含类信息的乱码字节(如“\xac\xed\x00\x05t\x00\x08user:1001”),而非明文字符串"user:1001",二者字节序列不匹配。

RedisTemplate.delete() 传入的 key 找不到对应数据
根本原因不是 delete 失效,而是你传进去的 key 字符串和 Redis 里实际存的 key 字节序列不一致。Spring Boot 默认用 JdkSerializationRedisSerializer 序列化 key,结果存进去的是带类信息、Base64 编码的乱码字节,比如 ACED0005740008757365723A31303031,而你代码里写的是 "user:1001" —— 它俩压根不是同一个东西。
常见表现:
-
redisTemplate.hasKey("user:1001")返回false,但redis-cli keys "user:*"能查到 - 用
redisTemplate.delete("user:1001")没报错也没效果 - 在 redis-cli 里用
get "user:1001"显示(nil),但用get "\xac\xed\x00\x05t\x00\x08user:1001"才能取到值(十六进制显示)
为什么 redisTemplate.keys("user:*") 返回空集合
因为 keys() 是按原始字节匹配前缀的,而默认序列化后的 key 开头是 \xac\xed\x00\x05t\x00\x08(JDK 序列化头),后面才拼上你的字符串。所以 "user:*" 这个 pattern 根本匹配不到任何以 user: 开头的原始字节流。
解决路径只有一条:让 key 的序列化方式回归纯字符串。必须显式配置:
redisTemplate.setKeySerializer(new StringRedisSerializer())redisTemplate.setHashKeySerializer(new StringRedisSerializer())- 如果用了
RedisCacheManager,还要同步配cacheConfiguration.keyPrefix()或禁用自动前缀
注意:改完后旧 key 不会自动迁移,历史数据仍需手动清理或兼容处理。
Spring Boot 自动加双冒号 :: 导致前缀失效
即使你用了 StringRedisSerializer,Spring Boot 2.0+ 的 RedisCacheManager 默认会在 key 前额外拼一个 ::,比如你设 cacheName="user"、key="1001",最终存的是 user::1001。这时候你调用 redisTemplate.keys("user:*") 依然为空——得写成 redisTemplate.keys("user::*") 才行。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
绕过方式有二:
- 初始化
RedisCacheConfiguration时调用.disableKeyPrefix() - 或者自定义
RedisCacheManager,把cacheDefaults().keyPrefix(...)设为空或显式控制
别依赖 @CacheEvict(key = "#id") 自动推导——它的 key 构造逻辑和 redisTemplate.keys() 不是一套,容易错位。
用 scan + unlink 安全批量删前缀 key
别用 redisTemplate.keys("user::*") 然后 delete(keys),这在大数据量下会阻塞 Redis 主线程。正确姿势是分批 scan:
Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> result = new HashSet<>();
Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions()
.match("user::*")
.count(1000)
.build());
while (cursor.hasNext()) {
result.add(new String(cursor.next(), StandardCharsets.UTF_8));
}
return result;
});
if (!keys.isEmpty()) {
redisTemplate.unlink(keys); // 注意用 unlink 而非 delete,异步更安全
}
关键点:
-
scan是非阻塞命令,count建议设 100–1000,太小效率低,太大内存压力高 - 必须用
unlink,它把删除操作丢给后台线程,避免主线程卡住 - pattern 必须和真实存储的 key 完全一致——包括是否含
::、大小写、编码空格等
最常被忽略的,是不同服务间序列化策略不统一:A 服务用 StringRedisSerializer,B 服务没配,还用默认 JDK 序列化,结果 A 删 B 的 key 永远失败。上线前务必核对所有服务的 RedisTemplate 配置是否一致。

















