您好,登录后才能下订单哦!
密码登录
            
            
            
            
        登录注册
            
            
            
        点击 登录注册 即表示同意《亿速云用户服务条款》
        # SpringBoot中怎么使用Redis对单个对象进行自动缓存更新删除
## 目录
1. [Redis与SpringBoot集成概述](#redis与springboot集成概述)
2. [基础环境配置](#基础环境配置)
3. [实体类设计与序列化](#实体类设计与序列化)
4. [Repository层缓存实现](#repository层缓存实现)
5. [Service层缓存逻辑](#service层缓存逻辑)
6. [缓存自动更新机制](#缓存自动更新机制)
7. [缓存删除策略](#缓存删除策略)
8. [事务一致性处理](#事务一致性处理)
9. [高级特性与性能优化](#高级特性与性能优化)
10. [常见问题解决方案](#常见问题解决方案)
11. [完整代码示例](#完整代码示例)
12. [总结与最佳实践](#总结与最佳实践)
---
## Redis与SpringBoot集成概述
Redis作为高性能的键值存储系统,在SpringBoot中主要通过Spring Data Redis模块实现集成。对于单个对象的缓存管理,我们需要关注以下几个核心点:
1. **序列化方案**:选择适合业务场景的序列化方式(Jackson2Json/Java原生等)
2. **缓存粒度控制**:精确控制单个对象的缓存操作
3. **一致性保证**:确保数据库与缓存的数据一致性
4. **过期策略**:合理设置缓存过期时间
### 为什么需要对象级缓存?
- 避免频繁访问数据库获取相同数据
- 降低数据库负载压力
- 提升系统响应速度(Redis访问速度可达微秒级)
---
## 基础环境配置
### 1. 添加Maven依赖
```xml
<dependencies>
    <!-- Spring Boot Starter Data Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <!-- 连接池依赖 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    
    <!-- 如果使用JSON序列化 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 
    database: 0
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: -1ms
@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用Jackson2JsonRedisSerializer序列化value
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), 
                               ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(om);
        
        // String序列化用于key
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        
        template.afterPropertiesSet();
        return template;
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    private String username;
    private String email;
    private LocalDateTime createTime;
    
    // 注意:实体类需要实现Serializable接口
    // 如果使用JSON序列化则不需要
}
public class CacheKeyGenerator {
    public static String generateEntityKey(Class<?> clazz, Object id) {
        return String.format("%s::%s", clazz.getSimpleName(), id);
    }
    
    // 示例:生成User:1格式的key
    public static String generateUserKey(Long userId) {
        return generateEntityKey(User.class, userId);
    }
}
@Repository
@CacheConfig(cacheNames = "user")
public interface UserRepository extends JpaRepository<User, Long> {
    
    @Cacheable(key = "#id")
    Optional<User> findById(Long id);
    
    @CachePut(key = "#user.id")
    default User saveWithCache(User user) {
        return save(user);
    }
    
    @CacheEvict(key = "#id")
    void deleteById(Long id);
}
@Component
public class UserCacheDao {
    private static final String USER_KEY_PREFIX = "User::";
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void cacheUser(User user) {
        String key = USER_KEY_PREFIX + user.getId();
        redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);
    }
    
    public Optional<User> getCachedUser(Long userId) {
        String key = USER_KEY_PREFIX + userId;
        return Optional.ofNullable((User)redisTemplate.opsForValue().get(key));
    }
    
    public void evictUser(Long userId) {
        String key = USER_KEY_PREFIX + userId;
        redisTemplate.delete(key);
    }
}
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    
    private final UserRepository userRepository;
    private final UserCacheDao userCacheDao;
    
    @Override
    @Transactional(readOnly = true)
    public User getUserById(Long id) {
        // 先查缓存
        Optional<User> cachedUser = userCacheDao.getCachedUser(id);
        if (cachedUser.isPresent()) {
            return cachedUser.get();
        }
        
        // 缓存未命中则查数据库
        User user = userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("User not found"));
        
        // 写入缓存
        userCacheDao.cacheUser(user);
        return user;
    }
    
    @Override
    @Transactional
    public User updateUser(User user) {
        User updated = userRepository.save(user);
        userCacheDao.cacheUser(updated); // 更新缓存
        return updated;
    }
    
    @Override
    @Transactional
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
        userCacheDao.evictUser(id); // 删除缓存
    }
}
@Service
@CacheConfig(cacheNames = "user")
public class CacheAnnotatedUserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Cacheable(key = "#id")
    @Transactional(readOnly = true)
    public User getById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("User not found"));
    }
    
    @CachePut(key = "#user.id")
    @Transactional
    public User update(User user) {
        return userRepository.save(user);
    }
    
    @CacheEvict(key = "#id")
    @Transactional
    public void delete(Long id) {
        userRepository.deleteById(id);
    }
}
@Component
public class UserEntityListener {
    
    @Autowired
    private UserCacheDao userCacheDao;
    
    @PostUpdate
    @PostPersist
    public void postUpdate(User user) {
        // 实体更新后自动刷新缓存
        userCacheDao.cacheUser(user);
    }
    
    @PostRemove
    public void postRemove(User user) {
        // 实体删除后自动清除缓存
        userCacheDao.evictUser(user.getId());
    }
}
// 定义事件
public class UserCacheEvictEvent extends ApplicationEvent {
    private Long userId;
    
    public UserCacheEvictEvent(Object source, Long userId) {
        super(source);
        this.userId = userId;
    }
    // getter...
}
// 事件监听器
@Component
public class UserCacheListener {
    
    @Autowired
    private UserCacheDao userCacheDao;
    
    @EventListener
    public void handleUserCacheEvict(UserCacheEvictEvent event) {
        userCacheDao.evictUser(event.getUserId());
    }
}
// 在Service中发布事件
@Service
public class UserEventService {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void deleteUserWithEvent(Long id) {
        // ...业务逻辑
        eventPublisher.publishEvent(new UserCacheEvictEvent(this, id));
    }
}
| 策略类型 | 优点 | 缺点 | 
|---|---|---|
| 主动删除 | 实时性强,一致性高 | 增加系统复杂度 | 
| 被动过期 | 实现简单 | 存在短暂不一致 | 
public void evictUserCache(Long userId) {
    // 1. 删除本地缓存(如Caffeine)
    localCache.invalidate(userId);
    
    // 2. 删除Redis缓存
    redisTemplate.delete(CacheKeyGenerator.generateUserKey(userId));
    
    // 3. 发布缓存删除事件(用于集群环境)
    redisTemplate.convertAndSend("cache_evict", "User:" + userId);
}
@Transactional
public void updateUserWithDoubleDelete(User user) {
    // 第一次删除
    userCacheDao.evictUser(user.getId());
    
    // 更新数据库
    userRepository.save(user);
    
    // 延迟第二次删除
    CompletableFuture.runAsync(() -> {
        try {
            Thread.sleep(500); // 延迟500ms
            userCacheDao.evictUser(user.getId());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
}
@Transactional
public User transactionalUpdate(User user) {
    // 1. 先更新数据库
    User updated = userRepository.save(user);
    
    // 2. 再更新缓存(在同一个事务中)
    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                userCacheDao.cacheUser(updated);
            }
        });
    
    return updated;
}
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100))
public void safeCacheUpdate(User user) {
    try {
        userCacheDao.cacheUser(user);
    } catch (Exception e) {
        log.error("缓存更新失败,准备重试", e);
        throw e; // 触发重试
    }
}
@Recover
public void recoverCacheUpdate(Exception e, User user) {
    log.error("缓存更新最终失败,记录日志等待人工处理", e);
    // 可以将失败记录存入数据库或消息队列
}
public User getHotspotUser(Long id) {
    String lockKey = "UserLock:" + id;
    User user = userCacheDao.getCachedUser(id).orElse(null);
    
    if (user == null) {
        // 获取分布式锁
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
        
        if (Boolean.TRUE.equals(locked)) {
            try {
                // 双重检查
                user = userCacheDao.getCachedUser(id).orElse(null);
                if (user == null) {
                    user = userRepository.findById(id).orElseThrow();
                    userCacheDao.cacheUser(user);
                }
            } finally {
                redisTemplate.delete(lockKey);
            }
        } else {
            // 未获取到锁,短暂等待后重试
            try {
                Thread.sleep(100);
                return getHotspotUser(id);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException(e);
            }
        }
    }
    return user;
}
@Component
public class UserBloomFilter {
    private final BloomFilter<Long> bloomFilter;
    
    public UserBloomFilter(RedisTemplate<String, Object> redisTemplate) {
        this.bloomFilter = BloomFilter.create(
            Funnels.longFunnel(), 
            1000000, 
            0.01);
    }
    
    public void addUser(Long id) {
        bloomFilter.put(id);
    }
    
    public boolean mightContain(Long id) {
        return bloomFilter.mightContain(id);
    }
}
// 在Service中使用
public User getUserWithBloomFilter(Long id) {
    if (!userBloomFilter.mightContain(id)) {
        throw new EntityNotFoundException("User not exists");
    }
    return getUserById(id);
}
# application.yml 配置随机过期时间
spring:
  cache:
    redis:
      time-to-live: 3600000 # 基础过期时间1小时
      randomized-ttl-offset: 600000 # ±10分钟随机偏移
@Cacheable(value = "user", key = "#id", 
           unless = "#result == null") // 结果为null不缓存
public User getByIdNullable(Long id) {
    return userRepository.findById(id).orElse(null);
}
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
    return RedisCacheConfiguration.defaultCacheConfig()
        .serializeValuesWith(SerializationPair.fromSerializer(
            new GenericJackson2JsonRedisSerializer(objectMapper())))
        .disableCachingNullValues();
}
@Bean
public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(DeserializationFeature.FL_ON_UNKNOWN_PROPERTIES, false);
    return mapper;
}
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        return RedisCacheManager.builder(factory)
            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
            .transactionAware()
            .build();
    }
    
    @Bean
    public KeyGenerator userKeyGenerator() {
        return (target, method, params) -> 
            "User::" + params[0];
    }
}
@Service
public class CompleteUserService {
    
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private UserCacheDao userCacheDao;
    @Autowired
    private UserBloomFilter userBloomFilter;
    
    @Transactional(readOnly = true)
    public User getCompleteUser(Long id) {
        // 布隆过滤器检查
        if (!userBloomFilter.mightContain(id)) {
            throw new EntityNotFoundException();
        }
        
        // 尝试从缓存获取
        return userCacheDao.getCachedUser(id)
            .orElseGet(() -> {
                User user = userRepository.findById(id)
                    .orElseThrow(() -> new EntityNotFoundException());
                
                // 异步更新缓存
                CompletableFuture.runAsync(() -> 
                    userCacheDao.cacheUser(user));
                
                return user;
            });
    }
    
    @Transactional
    public User updateCompleteUser(User user) {
        User updated = userRepository.save(user);
        
        // 事务提交后更新缓存
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    userCacheDao.cacheUser(updated);
                    userBloomFilter.addUser(updated.getId());
                }
            });
        
        return updated;
    }
}
| 模式 | 适用场景 | 实现复杂度 | 
|---|---|---|
| Cache-Aside | 大多数读多写少场景 | 中等 | 
| Read-Through | 需要抽象缓存层 | 较高 | 
| Write-Through | 强一致性要求 | 高 | 
| Write-Behind | 允许最终一致性 | 最高 | 
// 通过Redis命令监控
Info stats  // 查看命中率
Info memory // 内存使用情况
Slowlog get // 慢查询日志
通过合理的Redis缓存设计,可以使SpringBoot应用的性能提升10-100倍,特别是在高并发读取场景下。建议在实际项目中结合具体业务需求进行调整,并做好监控和容量规划。 “`
注:本文档实际字数约9500字,包含了从基础配置到高级优化的完整内容。实际使用时可根据项目需求选择适合的技术方案。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。