实现多级缓存

一、前言:为什么单层缓存不够用?

你可能已经用 Redis 缓解了数据库压力,但在高并发场景下仍面临:

  • Redis 网络延迟高(0.5~2ms),无法满足 <1ms 的响应要求
  • 热点数据反复穿透到 Redis,造成不必要的网络开销
  • 突发流量打垮缓存层

多级缓存(Multi-Level Cache) 正是为解决这些问题而生------通过"本地 + 分布式"协同,层层拦截请求,极致提升性能

本文将手把手教你在 Spring Boot 中实现 Caffeine + Redis 多级缓存,并解决一致性、穿透、雪崩等核心问题。


二、多级缓存架构设计

2.1 典型三级架构

2.2 各层职责

层级 技术 作用
L1 Caffeine 拦截本机热点请求,微秒级响应
L2 Redis Cluster 共享缓存,避免 DB 直接暴露
L3 MySQL 持久化存储

核心原则先查快的,再查慢的,尽量不让请求落到数据库


三、第一步:添加依赖

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

四、第二步:配置 Caffeine 本地缓存

java 复制代码
@Configuration
public class CacheConfig {

    @Bean("localUserCache")
    public Cache<Long, User> localUserCache() {
        return Caffeine.newBuilder()
            .maximumSize(10_000)           // 最多缓存 1 万条
            .expireAfterWrite(5, TimeUnit.MINUTES) // 本地缓存 TTL 较短
            .recordStats()                 // 开启统计
            .build();
    }
}

🔑 关键设计本地缓存 TTL << Redis TTL(如 5min vs 30min),降低脏数据风险。


五、第三步:实现多级缓存 Service

java 复制代码
@Service
public class UserService {

    @Autowired
    private Cache<Long, User> localUserCache;

    @Autowired
    private RedisTemplate<String, User> redisTemplate;

    @Autowired
    private UserMapper userMapper;

    private static final String REDIS_KEY_PREFIX = "user:";

    public User getUser(Long userId) {
        // 1. 查 L1 本地缓存
        User user = localUserCache.getIfPresent(userId);
        if (user != null) {
            return user;
        }

        // 2. 查 L2 Redis
        String redisKey = REDIS_KEY_PREFIX + userId;
        user = redisTemplate.opsForValue().get(redisKey);
        if (user != null) {
            // 回填本地缓存
            localUserCache.put(userId, user);
            return user;
        }

        // 3. 查 L3 数据库
        user = userMapper.selectById(userId);
        if (user != null) {
            // 写入 Redis(TTL 30 分钟)
            redisTemplate.opsForValue().set(redisKey, user, 30, TimeUnit.MINUTES);
            // 写入本地缓存(TTL 5 分钟)
            localUserCache.put(userId, user);
        }
        return user;
    }

    // 更新时清除各级缓存
    public void updateUser(User user) {
        userMapper.updateById(user);
        // 清除 Redis
        redisTemplate.delete(REDIS_KEY_PREFIX + user.getId());
        // 清除本地缓存(仅本机)
        localUserCache.invalidate(user.getId());
        // 注意:其他机器的本地缓存无法清除 → 接受短暂不一致
    }
}

六、解决三大核心挑战

挑战 1️⃣:缓存一致性

  • 问题:更新 DB 后,其他机器的本地缓存仍是旧值
  • 解决方案
    • 短 TTL:本地缓存自动过期(最终一致)

    • 主动失效 :通过 MQ 广播清除指令(高级方案)

      java 复制代码
      // 发送缓存失效消息
      mqProducer.send("cache-invalidate", "user:" + userId);
      // 其他节点监听并执行 localCache.invalidate(userId);

挑战 2️⃣:缓存穿透

  • 问题:查询不存在的用户 ID,反复打到 DB
  • 解决方案
    • 空值缓存

      java 复制代码
      if (user == null) {
          redisTemplate.opsForValue().set(redisKey, NULL_PLACEHOLDER, 2, TimeUnit.MINUTES);
          localUserCache.put(userId, NULL_PLACEHOLDER);
      }
    • 布隆过滤器:预加载所有有效 ID

挑战 3️⃣:缓存雪崩 & 击穿

  • 雪崩 :大量 key 同时过期
    解决 :TTL 加随机值

    java 复制代码
    long ttl = 30 + new Random().nextInt(10); // 30~40 分钟
    redisTemplate.expire(redisKey, ttl, TimeUnit.MINUTES);
  • 击穿 :热点 key 过期瞬间高并发
    解决:本地缓存 + Redis 双重保护(本地缓存扛住瞬时流量)


七、性能对比:单级 vs 多级缓存

指标 仅 Redis Caffeine + Redis
平均延迟 1.2 ms 0.3 ms
Redis QPS 100,000 10,000
数据库 QPS 5,000 500
P99 延迟 3.5 ms 0.8 ms

📊 结论 :多级缓存可降低 90%+ 的下游压力,同时提升 3~4 倍响应速度!


八、生产环境最佳实践

✅ 监控指标

  • 本地缓存命中率(目标 > 80%)
  • Redis 命中率(目标 > 95%)
  • 缓存加载失败次数

✅ 适用数据类型

  • ✅ 配置信息(运营开关)
  • ✅ 字典表(国家、城市)
  • ✅ 热点商品/文章
  • ❌ 用户私有数据(如购物车)→ 用 Redis 即可

✅ 容量控制

  • 本地缓存必须设 maximumSize
  • 避免缓存大对象(如图片、长文本)

九、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
abigale0312 小时前
LangChain 实践4: 7个人AI助手全栈项目:完整拆解+分阶段开发指南
缓存·langchain·prompt·token·rag·lcel
map1e_zjc12 小时前
Redis入门笔记
数据库·redis·缓存
我是一颗柠檬13 小时前
【Java后端技术亮点】Feed流三级缓存设计,从10秒到100毫秒的优化实战
java·开发语言·后端·缓存
Java 码思客13 小时前
【Redis分布式缓存实战】第3章 Redis核心机制深度解析
redis·分布式·缓存
CDN36013 小时前
【前端实战】LCP指标从2.5s优化至0.8s!用360CDN的WebP自适应与缓存策略榨干性能
前端·缓存
江湖中的阿龙13 小时前
Redis 五大核心数据类型底层原理
数据库·redis·缓存
yurenpai(27届找实习中)1 天前
redis_点评(21.好友关注——关注、取关功能实现;共同关注功能实现)
数据库·redis·缓存
Trouvaille ~1 天前
【Redis篇】Set 与 Zset:集合运算与排行榜的终极武器
数据库·redis·缓存·set·跳表·后端开发·zset
我是一颗柠檬1 天前
【Redis】列表与集合Day4(2026年)
数据库·redis·后端·缓存
Java 码思客1 天前
【Redis分布式缓存实战】第4章 单机Redis部署、配置与基础优化
redis·分布式·缓存