我有一个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
做些什么?
答案 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
对象来完成此操作。它提供了为每个地图条目设置ttl
和maxIdle
的功能。例如:
// 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
级别提供了值。以下步骤将帮助您在cache
和default
级别配置TTL时间。
<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>
cache:
host: localhost
port: 6379
default-ttl: 6000
caches-ttl:
photo: 3600
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