Java Spring Data Redis 过期密钥

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34893279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:06:19  来源:igfitidea点击:

Spring Data Redis Expire Key

javaspringhibernateredis

提问by Akash Chavda

I have a One Spring Hibernate Application. In my application, Recently i am implemented Spring data Redis.

我有一个 One Spring Hibernate 应用程序。在我的应用程序中,最近我实现了 Spring data Redis。

spring-servlet.xml
<!-- redis connection factory -->
<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/>

<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" 
    p:connection-factory-ref="jedisConnFactory"/>

And this redisTemplateuse in my ServiceImpl class.

redisTemplate在我的 ServiceImpl 类中使用。

RedisServiceImpl

@Autowired
private RedisTemplate<String, T> redisTemplate;

public RedisTemplate<String, T> getRedisTemplate() {
    return redisTemplate;
}

public void setRedisTemplate(RedisTemplate<String, T> redisTemplate) {
    this.redisTemplate = redisTemplate;
}

Now I added data in redisServer like this

现在我像这样在 redisServer 中添加了数据

public void putData(String uniqueKey, String key, Object results) {

    redisTemplate.opsForHash().put(uniqueKey, key, results);
}

Now i want to remove Expire key.

现在我想删除过期键。

I search in Google, But in google all are saying like this

我在谷歌搜索,但在谷歌都是这样说的

redisTemplate.expire(key, timeout, TimeUnit);

In this expire method, We need to provide uniqueKeyinstead of key. But I need to Expire keyinstead of uniqueKey.

在这个 expire 方法中,我们需要提供uniqueKey而不是key. 但我需要 Expirekey而不是uniqueKey.

So Please help me what can i do for expire Key?

所以请帮助我我能做些什么来过期Key

回答by Santosh Joshi

Actually You cannot expire or set the TTL for individual keys inside the Redis Hash. You can only expire or set TTL the complete hash. if you want to support this you have to change your data structure.

实际上,您不能为 Redis 哈希中的单个键过期或设置 TTL。您只能过期或将 TTL 设置为完整的散列。如果你想支持这一点,你必须改变你的数据结构。

Here is the link for why it is not possible; and below are some excerpts from Redis expire

这是为什么不可能的链接;以下是Redis expire 的一些摘录

As far as i know redis cares for performance than features. It will defeat the purpose of memory efficient hash implementation in redis. Since hash key-value fields are not always represented as full featured redis object (they could be stored as linear array when hash is small to save memory), so the hash key field cannot have a TTL.

据我所知,Redis 更关心性能而不是功能。这将违背 redis 中内存高效哈希实现的目的。由于哈希键值字段并不总是表示为全功能的 redis 对象(当哈希很小时,它们可以存储为线性数组以节省内存),因此哈希键字段不能有 TTL。

Also this link Allow to set an expiration on hash fieldmight help you to change your data structure to handle expiry

此链接允许在哈希字段上设置过期可能会帮助您更改数据结构以处理过期

回答by Nikita Koksharov

Actually you can do it with RedissonRedis Java Client using RMapCacheobject. It provides ability to set ttland maxIdleper map entry. Example:

实际上,您可以使用对象使用RedissonRedis Java Client 来实现RMapCache。它提供了设置ttlmaxIdle每个地图条目的能力。例子:

// implements java.util.concurrent.ConcurrentMap interface
RMapCache<String, SomeObject> map = redisson.getMapCache("anyMap");

// ttl = 10 minutes, 
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES);

// ttl = 10 minutes, maxIdleTime = 10 seconds
map.put("key1", new SomeObject(), 10, TimeUnit.MINUTES, 10, TimeUnit.SECONDS);

回答by Swadeshi

I am using Redis Version 3.2.100.

我正在使用 Redis 版本 3.2.100。

Instead of redis template,Use Redis Cache Manager, pass redistemplate to cacheManager and use its set expires property to which is basically map of String & Long , you can add cache name and set its expiry time i.e time to live (TTL).

代替redis 模板,使用Redis 缓存管理器,将 redistemplate 传递给 cacheManager 并使用其设置的 expires 属性,该属性基本上是 String & Long 的映射,您可以添加缓存名称并设置其到期时间,即生存时间(TTL)。

You can use setDefaultExpirationmethod of cacheManager to set same expiry time to all the cache.

您可以使用cacheManager 的setDefaultExpiration方法为所有缓存设置相同的到期时间。

@SuppressWarnings({ "rawtypes", "unused" })
@Configuration
@EnableCaching(proxyTargetClass = true, mode = AdviceMode.ASPECTJ, order = 1)
@PropertySource("classpath:/application.properties")
public class CacheConfigImpl extends CachingConfigurerSupport {

    private @Value("${redis.ip}") String redisHost;
    private @Value("${redis.port}") int redisPort;

     private static final Map<String, Long> cacheMap = new HashMap<String, Long>();
    static {
        cacheMap.put("method1cache", 600L);
        cacheMap.put("method2cache", 600L);
        cacheMap.put("method3cache", 800L);
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
        redisConnectionFactory.setHostName(CustomPropertyLoader.getProperty("redis.ip"));
        redisConnectionFactory.setPort(Integer.parseInt(CustomPropertyLoader.getProperty("redis.port")));
        return redisConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean(name = "RCacheManager")
    public CacheManager cacheManager(RedisTemplate redisTemplate) {

        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        cacheManager.setExpires(cacheMap);
        cacheManager.setUsePrefix(true);
        final String redis_client_name = CustomPropertyLoader.getProperty("redis.client.name");
        cacheManager.setCachePrefix(new RedisCachePrefix() {
            private final RedisSerializer<String> serializer = new StringRedisSerializer();
            private final String delimiter = ":";

            public byte[] prefix(String cacheName) {
                return this.serializer
                        .serialize(redis_client_name.concat(this.delimiter).concat(cacheName).concat(this.delimiter));
            }
        });
        return cacheManager;
    }
    }

回答by Andreas Gelever

You can adopt Quartz for this purpose (implementing ttl for a Redis record). If you use Spring Boot, it autoconfigures Schedulerfor you. Thus you can autowire it directly to your service layer.

为此,您可以采用 Quartz(为 Redis 记录实现 ttl)。如果您使用 Spring Boot,它会为您自动配置Scheduler。因此,您可以将其直接自动连接到您的服务层。

@Autowired
private Scheduler scheduler;

Then you need to implement a job like this (in this example I am using Spring Redis Data):

然后你需要实现一个这样的工作(在这个例子中我使用的是 Spring Redis Data):

@Slf4j
@Component
public class RemoveExpiredRecordJob implements Job {

@Autowired
public RedisRepository redisRepository;

@Override
public void execute(JobExecutionContext jobExecutionContext) {
    String key = jobExecutionContext
            .getJobDetail()
            .getKey()
            .getName();
    redisRepository.deleteById(key);
    log.info("Record removed due timeout :: {}", key);
}

}

}

Then you can encapsulate some logic for creating JobDetail and Trigger

然后就可以封装一些创建JobDetail和Trigger的逻辑

@Component
public class SchedulerComponentBuilder {

    public JobDetail getJobDetail (String key, Class<? extends org.quartz.Job> clazz) {
        return JobBuilder.newJob().ofType(clazz)
                .storeDurably(false)
                .withIdentity(key)
                .withDescription("This key will be removed from Redis store when time expires.")
                .build();
    }

    public Trigger getTrigger(int ttl, JobDetail jobDetail) {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.add(java.util.Calendar.SECOND, ttl);
        return TriggerBuilder.newTrigger().forJob(jobDetail)
                .withDescription("This trigger fires once to remove an expired record from Redis store.")
                .startAt(calendar.getTime())
                .build();
    }
}

And finally, right after you saved you record in Redis repository, you need to schedule a job for removal this record (uniqueKey) from it like this:

最后,在您将记录保存在 Redis 存储库中后,您需要安排一项作业以从中删除此记录 (uniqueKey),如下所示:

@Autowired
private SchedulerComponentBuilder schedulerComponentBuilder;

private void schedule(String uniqueKey, int ttl) {
    try {
        JobDetail jobDetail = schedulerComponentBuilder.getJobDetail(uniqueKey, RemoveExpiredRecordJob.class);
        Trigger jobTrigger = schedulerComponentBuilder.getTrigger(ttl, jobDetail);
        scheduler.scheduleJob(jobDetail,jobTrigger);
        log.info("Job is scheduled :: {}", jobDetail);
    } catch (SchedulerException e) {
        log.error("Filed to schedule a job {}", e);
        throw new RuntimeException(e);
    }
}

回答by Akanksha Sharma

To set TTL for keys, you may create multiple beans of cacheManager and set TTL for individual bean. Then as per your use, you can use required cachemanager. Here is what I have implemented.

要为键设置 TTL,您可以创建多个 cacheManager bean 并为单个 bean 设置 TTL。然后根据您的使用,您可以使用所需的缓存管理器。这是我已经实施的。

@Configuration("cacheConfig")
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport{


    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        System.out.println("redisConnectionFactory");
        JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();

        // Defaults
        redisConnectionFactory.setHostName("127.0.0.1");
        redisConnectionFactory.setPort(6379);
        return redisConnectionFactory;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
        System.out.println("redisTemplate");
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
        redisTemplate.setConnectionFactory(cf);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    @Bean
    @Primary
    public CacheManager cacheManager2(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        // Number of seconds before expiration. Defaults to unlimited (0)
        cacheManager.setDefaultExpiration(20);
        cacheManager.setUsePrefix(true);
        return cacheManager;
    }


    @Bean
    public CacheManager cacheManager1(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        // Number of seconds before expiration. Defaults to unlimited (0)
        cacheManager.setDefaultExpiration(60);
        cacheManager.setUsePrefix(true);
        return cacheManager;
    }

}

To use above created cachemanager beans,

要使用上面创建的缓存管理器 bean,

@Cacheable(value = "users", key = "#userId.toString()", cacheManager ="cacheManager2")
    @RequestMapping(value = "/{userId}", method = RequestMethod.GET)
    public User getUser(@PathVariable String userId) {
        LOG.info("Getting user with ID {}.: "+userId);
      return userService.fetchUserDataonUsers(userId);
    }


@Cacheable(value = "users", key = "#userId.toString()", cacheManager ="cacheManager1")
    @RequestMapping(value = "data/{userId}", method = RequestMethod.GET)
    public String getUserData(@PathVariable String userId) {
        LOG.info("Getting user with ID getUserData {}.: "+userId);
      return userService.fetchUserDataonUsers(userId).toString();
    }

when we define cacheManager ="cacheManager2"in @Cacheable, It will use TTL set for cacheManager2defined in configuration. Same goes for cacheManager1

当我们cacheManager ="cacheManager2"在 in 中定义时 @Cacheable,它将使用 TTL 设置用于cacheManager2在配置中定义。同样适用cacheManager1

回答by Abdullah Khan

I am using Spring Data Redis.I am using @Redishash(timeToLive=300)annotation to expire my Entities after 300 seconds.

我正在使用 Spring Data Redis。我正在使用@Redishash(timeToLive=300)注释在 300 秒后使我的实体过期。

Here is the excerpt from my pom.xml

这是我的摘录 pom.xml

...
...
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>
...
...
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
    </dependency>
...
...

My RedisConfig.class

我的 RedisConfig.class

@Configuration
@Log4j2
@EnableRedisRepositories(basePackageClasses = ConsentOTP.class)
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private Integer port;

    @Value("${spring.redis.password}")
    private String password;

    @Bean
    JedisConnectionFactory jedisConnectionFactory() {
        log.info("=================================================================");
        log.info("redis config : {} : {} ", host, port);
        log.info("=================================================================");

        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port);
        config.setPassword(RedisPassword.of(password));
        return new JedisConnectionFactory(config);
    }

}

And my entity class ConsentOtp.class

还有我的实体类 ConsentOtp.class

@RedisHash(value = "ConsentOTP", timeToLive = 300)
@Data
@NoArgsConstructor
public class ConsentOTP implements Serializable {

    private static final long serialVersionUID = 1708925807375596799L;

    private String id;
    private LocalDateTime timestamp;
    private String otp;

    public ConsentOTP(String personId, LocalDateTime timestamp, String otp) {
        this.id = personId;
        this.timestamp = timestamp;
        this.otp = otp;
    }
}

Here is my Redis repository

这是我的Redis存储库

public interface ConsentOtpRepository extends CrudRepository<ConsentOTP, String> {

}

回答by Prasanth Rajendran

Though I am late to the party posting this for the Posterity.

虽然我迟到了为后代发帖的聚会。

Setting TTLvalue in key level it is not possible, because org.springframework.data.redis.cache.RedisCacheManagerdoes not provide any methods to configure the TTL value in key despite they have provided it for the cachelevel. The following steps will help you configure the TTL time in a cacheand defaultlevel.

TTL在密钥级别设置值是不可能的,因为org.springframework.data.redis.cache.RedisCacheManager尽管他们为cache级别提供了它,但没有提供任何方法来配置密钥中的 TTL 值。以下步骤将帮助您在 acachedefaultlevel 中配置 TTL 时间。

  1. Adding required maven dependency.
  1. 添加所需的 Maven 依赖项。
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
  1. adding Redis configuration including TTL values for default level and cache level. Here we have set the TTL value to a sample cache called "photo".
  1. 添加 Redis 配置,包括默认级别和缓存级别的 TTL 值。在这里,我们将 TTL 值设置为名为“photo”的示例缓存。
cache:
  host: localhost
  port: 6379
  default-ttl: 6000
  caches-ttl:
    photo: 3600
  1. Adding RedisCacheManager configuration
  1. 添加RedisCacheManager配置
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

@Configuration
@EnableCaching
public class RedisCacheConfiguration extends CachingConfigurerSupport {

    @Autowired
    private CacheConfigurationProperties cacheConfigurationProperties = null;

    private org.springframework.data.redis.cache.RedisCacheConfiguration createCacheConfiguration(long timeoutInSeconds) {
        return org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(timeoutInSeconds));
    }

    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory redisConnectionFactory) {
        Map<String, org.springframework.data.redis.cache.RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        if (Objects.nonNull(cacheConfigurationProperties.getCachesTTL())) {
            for (Entry<String, String> cacheNameAndTimeout : cacheConfigurationProperties.getCachesTTL().entrySet()) {
                cacheConfigurations.put(cacheNameAndTimeout.getKey(), createCacheConfiguration(Long.parseLong(cacheNameAndTimeout.getValue())));
            }
        }
        return RedisCacheManager
                .builder(redisConnectionFactory)
                .cacheDefaults(createCacheConfiguration(Long.parseLong(cacheConfigurationProperties.getDefaultTTL())))
                .withInitialCacheConfigurations(cacheConfigurations).build();
    }

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(cacheConfigurationProperties.getHost());
      redisStandaloneConfiguration.setPort(Integer.parseInt(cacheConfigurationProperties.getPort()));
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }

    @Configuration
    @ConfigurationProperties(prefix = "cache")
    @Data
    class CacheConfigurationProperties {
        private String port;
        private String host;
        private String defaultTTL;
        private Map<String, String> cachesTTL;
    }
}

The complete documentation is available at Medium

完整的文档可在Medium 获得

回答by Erik Ghukasyan

I had a same problem. Difference was just in using Jedis client. I solved it changing postions of UniqueKey and Key. For your example it will be something like this:

我有同样的问题。不同之处在于使用 Jedis 客户端。我解决了它改变 UniqueKey 和 Key 的位置。对于您的示例,它将是这样的:

redisService.sadd(key, uniqueKey);
redis.expire(key, expirationTime);