【问题标题】:Spring boot caching with redis,key have \xac\xed\x00\x05t\x00\x06使用 redis 进行 Spring Boot 缓存,密钥有 \xac\xed\x00\x05t\x00\x06
【发布时间】:2017-01-12 11:27:45
【问题描述】:

我想使用 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 服务器出现一个奇怪的键

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"

Spring caching

weird-redis-key-with-spring-data-jedis

如何设置@Cacheable的Serializer,如StringRedisTemplate默认:

public StringRedisTemplate() {
    RedisSerializer<String> stringSerializer = new StringRedisSerializer();
    setKeySerializer(stringSerializer);
    setValueSerializer(stringSerializer);
    setHashKeySerializer(stringSerializer);
    setHashValueSerializer(stringSerializer);
}

我的应用程序.properties:

spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379

build.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 缓存配置如何?
  • 我的 application.properties 是 spring.redis.host=localhost spring.redis.password= spring.redis.port=6379

标签: java spring caching redis


【解决方案1】:

创建一个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中读取缓存值的方式。

【讨论】:

  • 默认RedisTemplate不会出现奇怪的key,因为序列化器是StringRedisSerializer。但是默认的 @Cacheable(value = "abc", key = "#key") 可能使用 defaultSerializer = new JdkSerializationRedisSerializer();定义自己的 cacheManager 并设置这个 redisTemplate 将解决我的问题。谢谢你的回答
【解决方案2】:

Spring 的缓存特性允许使用不同的缓存实现。其中之一是 Redis。它可以与RedisCacheManager 类一起使用。 Spring documentation 说:

如果 Redis 可用且已配置,RedisCacheManager 将自动配置。

这是我建议影响 Redis - 缓存 - 集成的方法:

  1. RedisCacheManager 定义为您自己的 bean。

  2. RedisTemplate 传递给RedisCacheManager 的构造函数。

我使用编程配置在 Internet 上找到了一个 example。还有一个example使用基于XML的配置。

【讨论】:

  • 有没有办法在 application.properties 或 yaml 文件中实现这个。不要用来写代码~
  • 我几乎都不知道,但我认为这是不可能的。我在 RedisCacheManager 上找不到允许它的注释。
  • 现在不推荐将 RedisTemplate 传递给 RedisCacheManager 的构造函数。有什么新方法可以阻止奇怪的密钥生成?
【解决方案3】:

喜欢mm759的回答:

  1. 自行定义 RedisCacheManager 为 bean。

    1. 将 RedisTemplate 传递给 RedisCacheManager 的构造函数。

这段代码将解决我的问题:

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 存储是这样的:

【讨论】:

  • 其已弃用..现在 RedisCacheManager 不允许 StringRedisTemplate 在参数中
【解决方案4】:

这是我的两分钱。 ??️

TL;DR
假设您正在寻找删除所有这些奇怪键的解决方案。不要浪费时间编写单行 bash 解决方案。这行不通,redis-cli KEYS "user*" | xargs redis-cli DEL(来源:How do I remove keys?)。您的密钥是二进制格式的管道它们不会产生预期的结果。扩展创建密钥的脚本的功能以删除它们(想想 Python、Java、Go 等)。或者,如果您负担得起,请删除所有密钥redis-cli flushall(来源:How to atomically delete keys matching a pattern using Redis

就将您的密钥作为二进制数据而言,我不确定有什么好处(重复,二进制密钥,而不是数据)。因为,它的空间效率和时间效率可能低于预期,至少根据这些帖子does-storing-plain-text-data-take-up-less-space-than-storing-the-equivalent-messare-binary-storage-methods-more-efficient-than-text-based-ones。当然,如果您故意转换了密钥。

如果您希望使用字符串格式的密钥,请在 Redis 模板上,将密钥序列化程序设置为 StringRedisSerializer,正如 @Rohith K 的答案中所指出的那样。这将使您能够使用上面引用的模式匹配命令删除键。

redisTemplate.setKeySerializer(new StringRedisSerializer());

最后,这里有更多关于这些有趣符号 "\xac\xed\x00\x05t\x00:" 是如何被添加到您的 Redis 密钥的讨论:

  1. Java creating Redis key and content with strange characters
  2. Spring Boot Redis store a list of POJOs as values
  3. Spring Redis Delete does not delete key

我希望这篇文章可以为您节省一些时间。快乐编码?

【讨论】:

    猜你喜欢
    • 2013-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多