我刚接触Redis,这可能是一个基本的问题.
我使用@Cacheable()
和@CacheEvict()
注释.当用户更新时,如果我通过id获取用户,它将获取缓存的(过时的)数据.当然,如果我使用@CacheEvict()
,这种情况就不会发生.
然而,我对@CacheEvict()
感到困惑,因为结果与我不使用它是一样的--那么使用它有什么意义呢?如果有一个过程需要3秒才能完成,那么使用CacheEvict()
也需要3秒.
以下是我的UserServiceImpl.java
个班级:
package com.example.demo.serviceImpl;
import lombok.AllArgsConstructor;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import com.example.demo.service.UserService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
@Service
@EnableCaching
@AllArgsConstructor
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
@Override
public User createUser(User user) {
return userRepository.save(user);
}
@Override
@CacheEvict(value = "users")
public User findUser(String userId) {
doLongRunningTask();
return userRepository.findById(userId).orElseThrow();
}
@Override
@Cacheable(value = "users")
public List<User> findAll() {
return (List<User>) userRepository.findAll();
}
@Override
@CacheEvict(value = "users", key = "#user.id")
public User updateUser(String userId, User user) {
doLongRunningTask();
user.setUpdatedAt(LocalDateTime.now());
return userRepository.save(user);
}
@Override
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(String userId) {
userRepository.deleteById(userId);
}
private void doLongRunningTask() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我的RedisConfig.java
个班级:
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import java.time.Duration;
import static org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair.fromSerializer;
@Configuration
public class RedisConfig {
@Value("${redis.host}")
private String redisHost;
@Value("${redis.port}")
private int redisPort;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName(redisHost);
configuration.setPort(redisPort);
return new LettuceConnectionFactory(configuration);
}
@Bean
public RedisCacheManager cacheManager() {
RedisCacheConfiguration cacheConfig = myDefaultCacheConfig(Duration.ofMinutes(10)).disableCachingNullValues();
return RedisCacheManager
.builder(redisConnectionFactory())
.cacheDefaults(cacheConfig)
.withCacheConfiguration("users", myDefaultCacheConfig(Duration.ofMinutes(5)))
.build();
}
private RedisCacheConfiguration myDefaultCacheConfig(Duration duration) {
return RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl(duration)
.serializeValuesWith(fromSerializer(new GenericJackson2JsonRedisSerializer()));
}
}
第一次获取数据需要3秒.下一次获取相同的数据需要5毫秒(这一次从Redis而不是postgres获取).然而,更新该用户并再次获取它时,会给出过时的数据,而不是新更新的用户,从而导致数据不一致.
这是我的model/User.java
名模特班
package com.example.demo.model;
import lombok.*;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
@Data
@Builder
@RedisHash("user")
public class User {
@Id
private String id;
private String name;
private Integer age;
}
我还有dto/UserDTO.java
个用于通过API将模型转换为REST响应/请求:
package com.example.demo.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO implements Serializable {
@JsonProperty(value = "id")
private String id;
@JsonProperty(value = "name")
private String name;
@JsonProperty(value = "age")
private Integer age;
}
多亏了@Max Kozlov,这个DTO类现在是Serializable
,所以Redis缓存可以正常工作.
感谢@Max Kozlov的新RedisCacheConfig.java
人如下所示:
package com.example.demo.config;
import com.example.demo.handler.DefaultCacheErrorHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import java.time.Duration;
@Configuration
@EnableCaching
public class RedisCacheConfig implements CachingConfigurer {
@Value("${redis.host}")
private String redisHost;
@Value("${redis.port}")
private int redisPort;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName(redisHost);
configuration.setPort(redisPort);
return new LettuceConnectionFactory(configuration);
}
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
return RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(15));
}
@Bean
@Override
public CacheErrorHandler errorHandler() {
return new DefaultCacheErrorHandler();
}
@Bean("longLifeCacheManager")
public CacheManager longLifeCacheManager() {
RedisCacheConfiguration defaultConfiguration = RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl(Duration.ofDays(90));
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory())
.cacheDefaults(defaultConfiguration)
.build();
}
@Primary
@Bean("shortLifeCacheManager")
public CacheManager shortLifeCacheManager() {
RedisCacheConfiguration defaultConfiguration = RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl(Duration.ofDays(1));
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory())
.cacheDefaults(defaultConfiguration)
.build();
}
}