Spring Boot下Redis缓存无法获取所有键?匹配异常问题咨询
问题原因分析
1. RedisTemplate默认序列化器导致keys("*")返回空
你使用的Spring Boot 1.5.6版本中,默认的RedisTemplate采用JdkSerializationRedisSerializer作为键和值的序列化器。这会带来两个关键影响:
- 当你调用
getTestObject("Joe")时,缓存的键会被序列化成JDK序列化后的字节数组(而非明文字符串"Joe")存储在Redis中。 - 执行
redisTemplate.keys("*")时,RedisTemplate会先把字符串"*"用相同的序列化器转成字节数组,再发送给Redis执行KEYS命令。Redis会基于这个序列化后的字节数组模式去匹配键,但实际存储的键是序列化后的"Joe"字节数组,两者没有匹配关系,因此返回0个键。
2. 对hasKey方法的行为误解
Redis的HASKEY命令是精确匹配键,完全不支持通配符。你测试中hasKey("J*e")返回true,说明你的Redis中确实存在一个序列化后的"J*e"键——大概率是你之前调用过getTestObject("J*e"),导致该键被缓存。而Jo*、*oe、*返回false,是因为Redis中没有对应的精确键。
3. 为什么hasKey("Joe")返回true?
当你调用hasKey("Joe")时,RedisTemplate会把字符串"Joe"用Jdk序列化器转成和缓存时一致的字节数组,去Redis中精确匹配,因此能找到对应的键,返回true。
解决方法
方法1:替换为字符串序列化器(推荐用于可视化需求)
如果你希望键以明文字符串形式存储,方便直接用Redis客户端查询,可以自定义RedisTemplate,指定键序列化器为StringRedisSerializer:
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // 设置键和哈希键为字符串序列化器 template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); // 值序列化器可根据需求选择,比如JSON序列化器 template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); template.afterPropertiesSet(); return template; } }
方法2:绕过序列化直接执行原生命令
如果你不想修改序列化器,可以通过RedisCallback直接执行原生KEYS命令,手动处理序列化:
RedisTemplate redisTemplate = (RedisTemplate) cacheManager.getCache("test").getNativeCache(); Set<Object> keys = redisTemplate.execute((RedisCallback<Set<Object>>) connection -> { // 直接发送字节数组形式的"*",避免RedisTemplate自动序列化 Set<byte[]> keyBytes = connection.keys("*".getBytes()); // 用键序列化器反序列化字节数组,得到原始键值 return keyBytes.stream() .map(redisTemplate.getKeySerializer()::deserialize) .collect(Collectors.toSet()); });
方法3:用SCAN替代KEYS(大键量场景推荐)
KEYS命令在Redis键数量较多时会阻塞服务器,推荐使用SCAN命令迭代查询:
RedisTemplate redisTemplate = (RedisTemplate) cacheManager.getCache("test").getNativeCache(); Set<Object> keys = new HashSet<>(); redisTemplate.execute((RedisCallback<Void>) connection -> { ScanOptions options = ScanOptions.scanOptions().match("*").count(100).build(); Cursor<byte[]> cursor = connection.scan(options); while (cursor.hasNext()) { keys.add(redisTemplate.getKeySerializer().deserialize(cursor.next())); } return null; });
内容的提问来源于stack exchange,提问作者Don Rhummy
相关产品推荐
相关产品推荐

