全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-18 10 分钟 ✍️ juanwangdev

Spring 缓存策略深度解析

缓存是提升系统性能的核心手段,Spring 提供了统一的缓存抽象层。

Spring Cache 核心注解

注解作用使用场景
@Cacheable查询缓存读操作
@CachePut更新缓存写操作后刷新
@CacheEvict删除缓存数据删除时
@Caching组合操作复杂缓存逻辑

基础配置

启用缓存

Java
@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

缓存配置类

Java
@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeKeysWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(factory)
            .cacheDefaults(config)
            .transactionAware()
            .build();
    }
}

@Cacheable 详解

基本使用

Java
@Service
public class UserService {

    @Cacheable(value = "users", key = "#id")
    public User findById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("User not found"));
    }

    @Cacheable(value = "users", key = "#name", condition = "#name.length() > 3")
    public User findByName(String name) {
        return userRepository.findByName(name);
    }
}

Key 表达式

表达式说明
#id参数名为 id
#p0第一个参数
#user.id参数对象的属性
#root.methodName方法名

@CachePut 与 @CacheEvict

更新缓存

Java
@Service
public class UserService {

    @CachePut(value = "users", key = "#user.id")
    public User update(User user) {
        return userRepository.save(user);
    }

    @CacheEvict(value = "users", key = "#id")
    public void delete(Long id) {
        userRepository.deleteById(id);
    }

    @CacheEvict(value = "users", allEntries = true)
    public void clearAll() {
        // 清空所有缓存
    }
}

缓存问题解决方案

1. 缓存穿透

Java
// 方案:缓存空值
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User findById(Long id) {
    User user = userRepository.findById(id).orElse(null);
    if (user == null) {
        // 缓存空对象,设置较短过期时间
        cacheTemplate.opsForValue().set("users:null:" + id, "", 5, TimeUnit.MINUTES);
    }
    return user;
}

2. 缓存击穿

Java
// 方案:互斥锁
@Cacheable(value = "users", key = "#id")
public User findByIdWithLock(Long id) {
    String lockKey = "lock:users:" + id;
    try {
        // 尝试获取锁
        Boolean acquired = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(acquired)) {
            User user = userRepository.findById(id).orElse(null);
            if (user != null) {
                redisTemplate.opsForValue().set("users:" + id, user, 30, TimeUnit.MINUTES);
            }
            return user;
        } else {
            Thread.sleep(100);
            return findById(id);
        }
    } finally {
        redisTemplate.delete(lockKey);
    }
}

3. 缓存雪崩

Java
// 方案:随机过期时间
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
        .entryTtl(Duration.ofMinutes(30).plusSeconds(ThreadLocalRandom.current().nextInt(300)));

    return RedisCacheManager.builder(factory)
        .cacheDefaults(config)
        .build();
}

多级缓存架构

Caffeine + Redis 二级缓存

Java
@Configuration
public class MultiCacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        // L1: 本地缓存 Caffeine
        CaffeineCacheManager caffeineManager = new CaffeineCacheManager();
        caffeineManager.setCaffeine(Caffeine.newBuilder()
            .initialCapacity(100)
            .maximumSize(1000)
            .expireAfterWrite(5, TimeUnit.MINUTES));

        // L2: 分布式缓存 Redis
        RedisCacheManager redisManager = RedisCacheManager.builder(factory)
            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30)))
            .build();

        return new CompositeCacheManager(caffeineManager, redisManager);
    }
}

缓存注解属性详解

@Cacheable 属性

Java
@Cacheable(
    value = "users",                    // 缓存名称
    key = "#id",                        // 缓存 key
    condition = "#id > 0",              // 缓存条件
    unless = "#result == null",         // 排除条件
    cacheManager = "customCacheManager" // 指定缓存管理器
)
public User findById(Long id) { ... }

自定义 Key 生成器

Java
@Bean
public KeyGenerator customKeyGenerator() {
    return (target, method, params) -> {
        StringBuilder sb = new StringBuilder();
        sb.append(target.getClass().getSimpleName());
        sb.append(":").append(method.getName());
        for (Object param : params) {
            sb.append(":").append(param.toString());
        }
        return sb.toString();
    };
}

@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
public User findById(Long id) { ... }

要点总结

要点说明
缓存注解@Cacheable/@CachePut/@CacheEvict
Key设计业务唯一标识,避免冲突
穿透防护缓存空值 + 布隆过滤器
击穿防护互斥锁 + 热点数据永不过期
雪崩防护随机过期时间 + 熔断降级
多级缓存Caffeine(L1) + Redis(L2)

📝 发现内容有误?点击此处直接编辑

← 上一篇 Spring 数据库优化实践
下一篇 → Spring 连接池配置详解
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库