我想使用Spring缓存@Cacheable来管理缓存。 而真正的缓存是redis。
我的代码是这样的:
@PostMapping("/post")
@CachePut(value = "abc", key = "#key")
public String putInRedis(@RequestParam String key, @RequestParam String value) {
saveInDB(key, value);
return value;
}
@GetMapping("/get")
@Cacheable(value = "abc", key = "#key")
public String queryRedis(@RequestParam String key) {
return findByKey(key);
}
在我收到帖子请求后
localhost:8080/post?key=key&value=value
redis服务器出现奇怪的key
127.0.0.1:6379> keys *
1) "abc:\xac\xed\x00\x05t\x00\x03key"
127.0.0.1:6379> GET "abc:\xac\xed\x00\x05t\x00\x03key"
"\xac\xed\x00\x05t\x00\x05value"
奇怪的-redis-key-with-spring-data-jedis
如何像StringRedisTemplate一样设置@Cacheable的Serializer默认:
public StringRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
setKeySerializer(stringSerializer);
setValueSerializer(stringSerializer);
setHashKeySerializer(stringSerializer);
setHashValueSerializer(stringSerializer);
}
我的应用程序属性:
spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379
构建.gradle
group 'io.freezhan'
version '1.0-SNAPSHOT'
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.4.0.RELEASE'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.13'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
apply plugin: 'java'
apply plugin: 'spring-boot'
sourceCompatibility = 1.5
repositories {
mavenCentral()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web") {
exclude module: "spring-boot-starter-tomcat"
}
compile("org.springframework.boot:spring-boot-starter-data-redis")
compile("org.springframework.boot:spring-boot-starter-jetty")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile 'org.projectlombok:lombok:1.16.10'
testCompile("junit:junit")
}
创建redis模板
private RedisTemplate<String, ?> createRedisTemplateForEntity() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(getRedisConnectionFactory());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
为什么它会创建一个奇怪的字符串作为键?
密钥是根据方法中存在的参数属性创建的,该方法被注释为可缓存。这就是spring从redis读取缓存值的方式。
就像mm759的回答:
自行将RedisCacheManager定义为bean。
这段代码将解决我的问题:
package io;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Created by freezhan on 16/9/5.
*/
@Configuration
public class CacheConfig {
@Autowired
private StringRedisTemplate redisTemplate;
@Bean
public CacheManager cacheManager() {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
return cacheManager;
}
}
Redis 存储如下:
这是我的两分钱。 🪙🏛️
TL;博士
假设您正在寻找一种解决方案来删除所有这些奇怪的键。不要浪费时间编写一行 bash 解决方案。这是行不通的,
redis-cli KEYS "user*" | xargs redis-cli DEL
(来源:如何删除钥匙?)。您的密钥采用二进制格式,通过管道传输它们不会产生预期的结果。扩展创建密钥的脚本的功能以删除它们(例如 Python、Java、Go 等)。或者,如果可以的话,删除所有密钥redis-cli flushall
(来源:如何使用 Redis 以原子方式删除与模式匹配的密钥
就将密钥作为二进制数据而言,我不确定有什么好处(重复,二进制密钥,而不是数据)。因为,至少根据这些帖子,它的空间效率和时间效率可能低于预期,存储纯文本数据占用的空间比存储更少-等效的混乱,是二进制存储方法比基于文本的方法更有效。当然,如果您故意转换了密钥。
如果您希望密钥采用字符串格式,请在 Redis 模板上将密钥序列化器设置为
StringRedisSerializer
,如 @Rohith K 的答案中所指出的。这将使您能够使用上面引用的模式匹配命令删除键。
redisTemplate.setKeySerializer(new StringRedisSerializer());
最后,这里有一些关于这些有趣的符号
"\xac\xed\x00\x05t\x00:"
如何添加到 Redis 键之前的更多讨论:
我希望这篇文章能为您节省一些时间。快乐编码🔥
8年后,我在Spring项目中使用Redission遇到了同样的问题。
在 redis 中设置值后,如下所示:
RMap<String, String> map = redissonClient.getMap("hash-key");
map.put(field, value);
字段和值都将以“>”为前缀。我用谷歌搜索但未能找到任何可用的解决方案。我查看了源代码,发现了一个奇怪的方法
/**
* Returns map instance by name
* using provided codec for both map keys and values.
*
* @param <K> type of key
* @param <V> type of value
* @param name - name of object
* @param codec - codec for keys and values
* @return Map object
*/
<K, V> RMap<K, V> getMap(String name, Codec codec);
因此我将代码更改为
RMap<String, String> map = redissonClient.getMap(CODE_ICON_URLS_KEY, StringCodec.INSTANCE);
砰,成功了。