Spring Boot Redis:实体更新后如何从数据库取数?@CacheEvict使用困惑
Redis缓存一致性问题:@CacheEvict注解未生效导致数据过时
我是Redis新手,这可能是个基础问题。我使用@Cacheable()和@CacheEvict()注解,当用户实体更新后,通过ID获取用户时仍会拿到缓存中的过时数据。我知道@CacheEvict()应该能解决这个问题,但我对它的作用感到困惑——使用它和不使用它的结果似乎一样,比如耗时3秒的长任务,使用该注解后还是需要3秒。
首次获取数据耗时3秒,再次获取同一数据仅需5毫秒(从Redis读取),但更新用户后再次获取会得到过时数据,导致数据不一致。
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())); } }
更新补充内容
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
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缓存正常工作。
更新后的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(); } }
内容的提问来源于stack exchange,提问作者busuu
相关产品推荐
相关产品推荐

