Spring Data Redis Expire Key

时间:2016-01-20 06:49:26

标签: java spring hibernate redis

我有一个Spring Spring Hibernate应用程序。在我的应用程序中,最近我实现了Spring数据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"/>

这个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;
}

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

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

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

现在我要删除过期密钥。

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

redisTemplate.expire(key, timeout, TimeUnit);

在此过期方法中,我们需要提供uniqueKey而不是key。 但我需要过期key而不是uniqueKey

那么请帮助我,我能为expire Key做些什么?

10 个答案:

答案 0 :(得分:6)

我使用的是Redis版本3.2.100。

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

您可以使用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;
    }
    }

答案 1 :(得分:3)

实际上,您可以使用Redisson框架(基于Redis的Data Grid for Java)使用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);

答案 2 :(得分:2)

实际上,您无法使Redis Hash中的各个键的TTL过期或设置。您只能将TTL过期或设置为完整哈希值。如果你想支持这个,你必须改变你的数据结构。

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

  

据我所知,redis关注性能而不是功能。它会   在redis中打败了内存高效哈希实现的目的。   由于散列键值字段并不总是表示为完整   特色redis对象(它们可以在哈希时存储为线性数组)   保存内存很小,所以哈希键字段不能有TTL。

此链接Allow to set an expiration on hash field可能会帮助您更改数据结构以处理到期

答案 3 :(得分:2)

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

@Autowired
private Scheduler scheduler;

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

@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);
}

}

然后你可以封装一些逻辑来创建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();
    }
}

最后,在您在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);
    }
}

答案 4 :(得分:2)

要设置密钥的TTL,您可以创建多个cacheManager bean并为单个bean设置TTL。然后根据您的使用情况,您可以使用所需的cachemanager。 这是我实施的内容。

@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;
    }

}

使用上面创建的cachemanager 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();
    }

当我们在cacheManager ="cacheManager2"中定义@Cacheable时,它将使用配置中定义的cacheManager2的TTL集。 cacheManager1

也是如此

答案 5 :(得分:1)

尽管我迟到要为后代张贴这篇文章。

无法在密钥级别设置TTL的值,因为org.springframework.data.redis.cache.RedisCacheManager没有提供任何用于配置密钥TTL值的方法,尽管它们已为cache级别提供了值。以下步骤将帮助您在cachedefault级别配置TTL时间。

  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. 添加Redis配置,包括用于默认级别和缓存级别的TTL值。在这里,我们将TTL值设置为称为“照片”的示例缓存。
cache:
  host: localhost
  port: 6379
  default-ttl: 6000
  caches-ttl:
    photo: 3600
  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;
    }
}

有关完整文档,请访问Medium

答案 6 :(得分:0)

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

这是我的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>
...
...

我的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);
    }

}

还有我的实体类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;
    }
}

这是我的Redis存储库

public interface ConsentOtpRepository extends CrudRepository<ConsentOTP, String> {

}

答案 7 :(得分:0)

我遇到了同样的问题。区别仅在于使用Jedis客户端。我解决了它更改UniqueKey和Key的位置的问题。 对于您的示例,它将是这样的:

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

答案 8 :(得分:0)

更新了@Akanksha Sharma 的 answer 与 Lettuce redis 客户端的代码。

@Bean
public RedisConnectionFactory lettuceConnectionFactory() {
    return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379));
}

@Bean
@Primary
public CacheManager cacheManager1(RedisConnectionFactory redisConnectionFactory) {
    
    return RedisCacheManager
            .builder(redisConnectionFactory)
            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60L)))
            .build();
    
}

@Bean
public CacheManager cacheManager2(RedisConnectionFactory redisConnectionFactory) {
    return RedisCacheManager
            .builder(redisConnectionFactory)
            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1L)))
            .build();
}

答案 9 :(得分:0)

嗨,不确定这是否仍然有效,但唯一正确的答案是

桑托什·乔希

https://stackoverflow.com/a/34896731/4222461

正如他正确指出的,您不能使 redis 哈希中的单个密钥过期,只能使哈希本身过期。

https://github.com/redis/redis/issues/1042

特别是https://github.com/redis/redis/issues/1042#issuecomment-45367109