springboot中怎么使用redis对单个对象进行自动缓存更新删除

发布时间:2021-08-05 14:20:45 作者:Leah
阅读:168
开发者专用服务器限时活动,0元免费领! 查看>>
# 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>

2. application.yml配置

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

3. RedisTemplate配置类

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

实体类设计与序列化

1. 基础实体类示例

@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序列化则不需要
}

2. 自定义缓存键生成策略

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层缓存实现

1. 使用Spring Cache注解

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

2. 自定义Redis操作类

@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层缓存逻辑

1. 基础服务实现

@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); // 删除缓存
    }
}

2. 使用Spring Cache注解的Service层

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

缓存自动更新机制

1. 数据库变更监听策略

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

2. 使用Spring事件机制

// 定义事件
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));
    }
}

缓存删除策略

1. 主动删除 vs 被动过期

策略类型 优点 缺点
主动删除 实时性强,一致性高 增加系统复杂度
被动过期 实现简单 存在短暂不一致

2. 多级缓存删除示例

public void evictUserCache(Long userId) {
    // 1. 删除本地缓存(如Caffeine)
    localCache.invalidate(userId);
    
    // 2. 删除Redis缓存
    redisTemplate.delete(CacheKeyGenerator.generateUserKey(userId));
    
    // 3. 发布缓存删除事件(用于集群环境)
    redisTemplate.convertAndSend("cache_evict", "User:" + userId);
}

3. 延迟双删策略

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

事务一致性处理

1. 缓存与数据库事务同步

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

2. 补偿机制设计

@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);
    // 可以将失败记录存入数据库或消息队列
}

高级特性与性能优化

1. 热点缓存重建优化

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

2. 布隆过滤器防穿透

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

常见问题解决方案

1. 缓存雪崩应对

# application.yml 配置随机过期时间
spring:
  cache:
    redis:
      time-to-live: 3600000 # 基础过期时间1小时
      randomized-ttl-offset: 600000 # ±10分钟随机偏移

2. 缓存穿透防护

@Cacheable(value = "user", key = "#id", 
           unless = "#result == null") // 结果为null不缓存
public User getByIdNullable(Long id) {
    return userRepository.findById(id).orElse(null);
}

3. 序列化异常处理

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

完整代码示例

1. 整合配置类

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

2. 完整Service实现

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

总结与最佳实践

1. 缓存模式选择建议

模式 适用场景 实现复杂度
Cache-Aside 大多数读多写少场景 中等
Read-Through 需要抽象缓存层 较高
Write-Through 强一致性要求
Write-Behind 允许最终一致性 最高

2. 性能优化检查清单

  1. [ ] 选择合适的序列化方案(优先JSON)
  2. [ ] 设置合理的过期时间(结合业务特点)
  3. [ ] 实现多级缓存(本地缓存+Redis)
  4. [ ] 添加防穿透机制(布隆过滤器/null值缓存)
  5. [ ] 对热点数据实现特殊处理(分布式锁重建)

3. 监控指标建议

// 通过Redis命令监控
Info stats  // 查看命中率
Info memory // 内存使用情况
Slowlog get // 慢查询日志

4. 最终建议

  1. 缓存粒度:保持细粒度,以单个对象为基础单位
  2. 一致性级别:根据业务需求选择合适的一致性强度
  3. 失败处理:设计完善的降级和补偿机制
  4. 容量规划:提前预估缓存容量并设置淘汰策略

通过合理的Redis缓存设计,可以使SpringBoot应用的性能提升10-100倍,特别是在高并发读取场景下。建议在实际项目中结合具体业务需求进行调整,并做好监控和容量规划。 “`

注:本文档实际字数约9500字,包含了从基础配置到高级优化的完整内容。实际使用时可根据项目需求选择适合的技术方案。

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

推荐阅读:
  1. 如何在springboot中对redis进行配置
  2. SpringBoot AOP控制Redis自动缓存和更新的示例

开发者交流群:

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

springboot redis

上一篇:php中如何将excel导出

下一篇:如何解决某些HTML字符打不出来的问题

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》
开发者交流群×