【Redis分布式缓存实战】第19章 多级缓存架构设计实战
2026/6/9 3:52:03 网站建设 项目流程

本地缓存(Caffeine/Guava)+ Redis分布式缓存架构搭建

一、架构概述

架构选型
  • 一级缓存(本地缓存)Caffeine(替代 Guava,Spring 官方推荐,性能碾压 Guava,内存级读写,速度纳秒级)
  • 二级缓存(分布式缓存)Redis(分布式共享缓存,解决本地缓存数据不一致问题)
  • 底层数据源:MySQL / 数据库
  • 框架:SpringBoot 2.x/3.x
核心流程
  1. 读流程:请求 → 查 Caffeine 本地缓存 → 命中直接返回 → 未命中查 Redis → 命中回写本地缓存 → 未命中查数据库 → 回写本地 + Redis 缓存
  2. 写流程:更新数据库 →删除Redis 缓存 → 发布 Redis 消息 → 所有服务节点删除本地 Caffeine 缓存(保证分布式一致性)
  3. 淘汰策略:本地缓存设置短过期时间+最大容量,Redis 设置过期时间+内存淘汰
核心优势
  • 极致性能:本地缓存无网络开销,扛高并发读
  • 降低压力:大幅减少 Redis 访问频次,避免 Redis 成为瓶颈
  • 高可用:Redis 宕机时,本地缓存可兜底服务

二、项目搭建(完整可运行)

核心 Maven 依赖
<?xml version="1.0" encoding="UTF-8"?> <dependencies> <!-- SpringBoot 缓存启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- Caffeine 本地缓存(替代Guava) --> <dependency> <groupId>com.github.benmanes.caffeine</groupId> <artifactId>caffeine</artifactId> </dependency> <!-- Redis 分布式缓存 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Redis 连接池 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- Web + 工具类 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
配置文件(application.yml)

统一配置 Redis 连接、Caffeine 本地缓存参数:

yaml

spring: # Redis 配置 redis: host: localhost port: 6379password: database: 0 # 连接池配置 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: -1ms # 缓存配置 cache: type: caffeine caffeine: # Caffeine 核心配置:初始容量/最大容量/写入后过期 spec: initialCapacity=100,maximumSize=10000,expireAfterWrite=5m # Redis 缓存过期时间(分钟) redis-expire: 10 # Redis 发布订阅主题(用于同步本地缓存) topic: cache: evict: topic
核心配置类
(1)RedisTemplate 序列化配置

解决 Redis 存储乱码、序列化冗余问题:

import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // String 序列化器(key) StringRedisSerializer stringSerializer = new StringRedisSerializer(); template.setKeySerializer(stringSerializer); template.setHashKeySerializer(stringSerializer); // JSON 序列化器(value) Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); jsonSerializer.setObjectMapper(om); template.setValueSerializer(jsonSerializer); template.setHashValueSerializer(jsonSerializer); template.afterPropertiesSet();return template; } }
(2)Caffeine 本地缓存实例配置
import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration public class CaffeineConfig { @Value("${spring.cache.caffeine.spec}") private String caffeineSpec; /** * 手动创建Caffeine本地缓存(推荐手动控制,比注解更灵活) */ @Beanpublic Cache<String, Object> caffeineCache() { return Caffeine.newBuilder() // 写入后5分钟过期(与配置文件一致) .expireAfterWrite(5, TimeUnit.MINUTES) // 最大缓存对象数.maximumSize(10000) // 初始容量 .initialCapacity(100).build(); } /** * Spring 注解式缓存管理器(可选) */ @Bean public CaffeineCacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCaffeine(Caffeine.from(caffeineSpec)); return cacheManager; } }
(3)Redis 发布订阅配置(解决分布式缓存一致性)

核心问题:分布式环境下,一个服务更新数据后,仅删除自己的本地缓存,其他服务的本地缓存仍为旧数据 → 通过 Redis 发布订阅同步所有节点的本地缓存

import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; @Configurationpublic class RedisPubSubConfig { @Value("${cache.topic}") private String cacheTopic; /** * 消息监听器:接收缓存删除消息 */ @Bean public MessageListenerAdapter listenerAdapter(CacheMessageListener listener) { return new MessageListenerAdapter(listener, "onMessage"); } /** * Redis 消息容器:绑定主题和监听器 */ @Bean public RedisMessageListenerContainer container(RedisConnectionFactory factory,MessageListenerAdapter adapter) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(factory); // 订阅缓存删除主题 container.addMessageListener(adapter, new PatternTopic(cacheTopic)); return container; } }

核心工具类
(1)缓存消息监听器(接收删除通知,清空本地缓存)
import com.github.benmanes.caffeine.cache.Cache; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.stereotype.Component; /** * 接收Redis发布的缓存删除消息,删除本地Caffeine缓存 */ @Component @RequiredArgsConstructor public class CacheMessageListener implements MessageListener { private final Cache<String, Object> caffeineCache; @Override public void onMessage(Message message, byte[] pattern) { // 解析需要删除的缓存key String cacheKey = new String(message.getBody()); // 删除本地缓存 caffeineCache.invalidate(cacheKey); System.out.println("分布式同步删除本地缓存,key:" + cacheKey); } }
(2)双缓存核心操作类(读写删)
import com.github.benmanes.caffeine.cache.Cache; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; import java.util.function.Function; /** * 一级缓存(Caffeine) + 二级缓存(Redis) 核心工具类 */ @Component @RequiredArgsConstructor public class DoubleCache { private final Cache<String, Object> caffeineCache; private final RedisTemplate<String, Object> redisTemplate; @Value("${cache.redis-expire}") private Integer redisExpire; @Value("${cache.topic}") private String cacheTopic; // ====================== 读缓存 ====================== public <T> T get(String key, Function<String, T> dbLoader) { // 1. 查一级本地缓存 (Caffeine)T value = (T) caffeineCache.getIfPresent(key); if (value != null) {System.out.println("本地缓存命中,key:" + key); return value; } // 2. 查二级分布式缓存(Redis) value = (T) redisTemplate.opsForValue().get(key); if (value != null) { System.out.println("Redis缓存命中,key:" + key); // 回写本地缓存 caffeineCache.put(key, value); return value; } // 3. 查数据库 System.out.println("数据库查询,key:" + key); value = dbLoader.apply(key); // 4. 回写两级缓存(解决缓存穿透:空值也缓存) if (value != null) { this.put(key, value); } else { // 空值缓存1分钟,避免穿透 redisTemplate.opsForValue().set(key, null, 1, TimeUnit.MINUTES); } return value; } // ====================== 写缓存 ====================== public void put(String key, Object value) { // 写本地缓存 caffeineCache.put(key, value); // 写Redis缓存(带过期时间,加随机值避免缓存雪崩) long expire = redisExpire + (long) (Math.random() * 5); redisTemplate.opsForValue().set(key, value, expire, TimeUnit.MINUTES); } // ====================== 删除缓存 ====================== public void evict(String key) { // 1. 删除Redis缓存 redisTemplate.delete(key); // 2. 发布Redis消息,通知所有节点删除本地缓存 redisTemplate.convertAndSend(cacheTopic, key); System.out.println("删除Redis缓存并发布同步消息,key:" + key); } }

业务层使用示例
import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class UserService { private final DoubleCache doubleCache; // 模拟数据库Mapper private final UserMapper userMapper; private static final String CACHE_KEY_PREFIX = "user:info:"; /** * 查询用户(双缓存读取) */ public User getUserById(Long userId) { String key = CACHE_KEY_PREFIX + userId; // Lambda表达式:数据库查询逻辑 return doubleCache.get(key, k -> userMapper.selectById(userId)); } /** * 更新用户(先更DB,再删缓存) */ public void updateUser(User user) { // 1. 优先更新数据库 userMapper.updateById(user); // 2. 删除缓存(核心:删除而非更新,避免并发不一致) String key = CACHE_KEY_PREFIX + user.getId(); doubleCache.evict(key); } /** * 删除用户 */ public void deleteUser(Long userId) { userMapper.deleteById(userId); String key = CACHE_KEY_PREFIX + userId; doubleCache.evict(key); } }

三、解决缓存三大问题

缓存穿透

问题:查询不存在的数据,直接击穿缓存访问数据库解决方案

  • 空值缓存:查询数据库无结果时,缓存null(短过期时间)
  • 布隆过滤器:高并发场景下,提前过滤不存在的 key
缓存击穿

问题:热点 key 过期,大量请求同时访问数据库解决方案

  • 互斥锁:查询数据库时加锁,保证只有一个请求查库
  • 热点 key 永不过期
缓存雪崩

问题:大量 key 同时过期,Redis 压力剧增解决方案

  • 过期时间加随机值
  • Redis 集群部署
  • 本地缓存兜底

四、关键注意事项

  1. 缓存更新策略先更新数据库,再删除缓存(绝对不要更新缓存,避免并发数据不一致)
  2. 过期时间:本地缓存过期时间 < Redis 过期时间(避免本地缓存脏数据)
  3. 本地缓存容量:Caffeine 设置maximumSize,防止 OOM
  4. 适用场景:读多写少、数据一致性要求不极高的业务(商品、配置、用户信息等)
  5. Guava 兼容:若需使用 Guava,仅需替换 Caffeine 依赖和配置,API 几乎一致

五、总结

  1. 本架构采用Caffeine(一级)+ Redis(二级)双缓存模式,兼顾性能与分布式一致性
  2. 通过Redis 发布订阅解决分布式环境下本地缓存同步问题
  3. 内置缓存穿透 / 击穿 / 雪崩防护,生产环境直接可用
  4. 核心原则:读缓存,写删缓存,保证数据最终一致性

多级缓存一致性、更新策略、失效同步方案

在分布式系统中,多级缓存本地缓存 L1 + Redis 分布式缓存 L2 + 数据库 DB)是性能优化的标配架构:

  • L1 本地缓存(Caffeine/Guava):应用内缓存,无网络开销、速度极致,但各节点独立、易不一致
  • L2 Redis 缓存:集群共享、大容量、高可用,有轻微网络开销;
  • DB:最终数据源,性能最低。

核心痛点:数据更新时,如何保证本地缓存、Redis、数据库三者的强一致性,尤其是多节点本地缓存的同步失效问题。

本文从缓存架构→更新策略→一致性问题→同步方案→生产实践全流程讲解,所有方案均为工业级可用。


一、标准多级缓存读写流程(基础)

读流程(通用,无一致性风险)
请求 → 查L1本地缓存 → 命中直接返回 ↓(未命中) 查L2 Redis缓存 → 命中则回写L1本地缓存,返回 ↓(未命中) 查DB → 回写L2 Redis + L1本地缓存 → 返回
写流程(一致性核心)

写操作会直接打破缓存一致性,所有策略都围绕「先操作数据库,再处理缓存」展开


二、Redis 多级缓存更新策略

生产环境99% 场景使用旁路缓存模式,另外两种仅特殊场景使用:

旁路缓存模式(Cache Aside)✅ 生产首选

核心规则:读多写少,先更新数据库,再删除缓存

  • 读:命中缓存直接返回,未命中查库并回写缓存;
  • 写:更新 DB → 删除 Redis 缓存 → 同步失效本地缓存
为什么是「删除缓存」而不是「更新缓存」?
  1. 避免无效写:数据频繁更新时,更新缓存会造成大量无用 IO;
  2. 避免脏数据:更新缓存可能导致并发读写的数据错乱。
为什么是「先 DB 后缓存」?

如果先删缓存再更新 DB,会出现致命脏数据:

读请求→缓存未命中→查DB旧数据 → 写请求→更新DB → 读请求→把旧数据写入缓存

最终缓存永远是旧数据,必须先更新 DB,再删缓存


写穿模式(Write Through)

核心:缓存与数据库同步写入,强一致

  • 写操作同时更新 DB 和 Redis,业务无感知;
  • 缺点:性能差,写请求延迟高,极少使用

写回模式(Write Back)

核心:先写缓存,异步批量刷库

  • 写操作只更新缓存,后台线程异步合并更新数据库;
  • 优点:写性能极致;
  • 缺点:存在数据丢失风险,一致性极差,仅用于日志等非核心数据

三、多级缓存一致性核心问题

Redis 是分布式共享缓存,删除一次即可全局生效;本地缓存是应用节点私有,这是一致性的最大痛点:

  1. 数据更新后,仅删除 Redis,未失效本地缓存 → 其他节点读取旧本地缓存;
  2. 并发读写场景:短暂的缓存脏数据;
  3. 缓存失效、消息丢失导致的最终不一致。

四、多级缓存失效同步方案(核心解决方案)

简单→可靠→无侵入排序,覆盖所有业务场景:

方案 1:短过期时间(兜底方案,最简单)
实现

本地缓存设置极短 TTL(如 3~5 秒),Redis 设置较长过期时间(如 30 分钟)。

流程

数据更新→更新 DB→删除 Redis→不主动删本地缓存→本地缓存到期自动失效。

优缺点
  • ✅ 零开发成本,无需中间件;
  • ❌ 一致性差,最多存在几秒脏数据;
  • 适用:对一致性要求极低的非核心数据(如统计数据、首页推荐)。

方案 2:消息通知同步(生产主流,实时性高)✅

核心:通过消息队列 / 发布订阅,通知所有应用节点删除本地缓存这是解决本地缓存同步的标准方案,分两种实现:

实现 1:Redis 发布 / 订阅(轻量,无额外中间件)
  1. 数据更新 → 更新 DB → 删除 Redis 缓存;
  2. 向 Redis 指定 Channel 发送缓存失效消息(key / 标识);
  3. 所有应用节点订阅该 Channel,收到消息后删除本地 L1 缓存
实现 2:专业 MQ(RabbitMQ/Kafka,高可靠)
  1. 流程同上,仅将 Redis Pub/Sub 替换为 MQ;
  2. 优势:消息持久化、不丢失、支持重试;
  3. 适用:核心业务数据。
完整流程
写请求 → 更新DB → 删除Redis → 发送失效消息 → 所有节点消费消息 → 删除本地缓存
优缺点
  • ✅ 实时性高、一致性好、架构简单;
  • ❌ Redis Pub/Sub 无持久化,消息可能丢失(MQ 可解决);
  • 适用:绝大多数微服务业务。

方案 3:Canal 监听 Binlog(无侵入,终极方案)✅

核心:不侵入业务代码,通过数据库 Binlog 同步缓存失效适合:多个服务写入同一张表、无法在业务代码中加缓存删除逻辑的场景。

流程
  1. DB 执行更新 / 插入→生成 Binlog;
  2. Canal 组件监听 Binlog,解析数据变更;
  3. Canal 推送变更消息→删除 Redis 缓存→通知所有节点删除本地缓存;
  4. 业务代码完全不关心缓存,零耦合。
优缺点
  • ✅ 业务无侵入、强一致、多写入口兼容;
  • ❌ 需要部署 Canal 服务,运维成本高;
  • 适用:核心交易数据、多服务共享表。

方案 4:延迟双删(解决并发脏数据)

针对并发读写的短暂脏数据问题,在方案 2 的基础上增加兜底:

更新DB → 删除Redis → 延迟200ms → 再次删除Redis → 发送消息删本地缓存

延迟时间略大于业务读请求的平均耗时,彻底清理并发写入的旧缓存。


方案 5:版本号机制(强一致兜底)

给缓存 key 增加版本号,彻底杜绝并发脏数据:

  1. 数据更新时,DB 版本号 + 1;
  2. 删除缓存时携带新版本号;
  3. 读缓存时校验版本号,不一致则重新查库加载。

五、方案对比与选型建议

方案

一致性

复杂度

成本

适用场景

本地缓存短过期

极低

非核心、读多写少数据

Redis Pub/Sub 通知

普通业务、非核心数据

MQ 通知同步

核心业务、要求高一致性

Canal Binlog 同步

极优

多写入口、强一致核心数据

延迟双删 + 版本号

最优

并发极高、零脏数据要求


六、生产级最佳实践(必看)

  1. 组合方案(万金油)本地缓存短 TTL 兜底+Redis Pub/Sub/MQ 实时同步+延迟双删,平衡一致性与性能。
  2. 缓存禁用自动刷新本地缓存只做主动删除,不做定时刷新,避免无效数据加载。
  3. 防护机制
  1. 缓存穿透:查询不存在的数据,缓存空值;
  2. 缓存雪崩:Redis 过期时间加随机偏移,本地缓存错开 TTL;
  3. 消息重复消费:消费端做幂等校验。
  1. 监控监控缓存命中率、本地缓存失效成功率、消息消费失败率。

七、总结

  1. 多级缓存架构:本地 L1 + Redis L2 + DB,读流程自上而下,写流程核心是先 DB 后删缓存
  2. 唯一推荐写策略:旁路缓存模式(Cache Aside);
  3. 本地缓存同步:生产用MQ/Redis 发布订阅,核心数据用Canal
  4. 并发一致性:用延迟双删 + 版本号兜底,杜绝脏数据。

这套方案是互联网公司通用的多级缓存一致性解决方案,兼顾性能、一致性与开发成本。

多级缓存抗高并发、降成本架构最佳实践

多级缓存的核心价值越靠近用户的缓存层级,速度越快、成本越低、抗并发能力越强。通过客户端缓存 → CDN → 本地堆缓存 → Redis 分布式缓存 → 数据库五层架构,将 99.9% 的请求拦截在前端 / 本地 / Redis 层,彻底避免高并发流量击穿到数据库,同时通过分层淘汰、资源优化大幅降低服务器 / 带宽 / 存储成本。

本文是生产环境落地的最佳实践,覆盖架构设计、读写策略、缓存一致性、成本优化、避坑全流程。


一、标准多级缓存分层架构(生产级)

请求距离从近到远访问速度从快到慢成本从低到高划分 5 层,读请求优先走上层,写请求只落地到数据库

表格

缓存层级

技术选型

速度

成本

核心作用

适用数据

1 级(客户端)

浏览器 LocalStorage/APP 内存 / 磁盘

纳秒级

0

拦截重复请求,不进服务端

静态配置、用户非敏感信息、静态页面

2 级(CDN)

阿里云 / 腾讯云 CDN、Cloudflare

微秒级

极低

抗静态资源流量峰值

图片、JS/CSS、静态 HTML、短视频

3 级(本地堆缓存)

Caffeine(首选)、Guava、JDK Map

纳秒级

0

扛热点数据极端并发,比 Redis 快 10~100 倍

秒杀商品、首页榜单、热榜、高频配置

4 级(Redis 分布式缓存)

Redis 集群(主从 + 哨兵 / 分片集群)

微秒级

跨服务共享缓存,支撑动态数据查询

商品详情、用户会话、订单缓存、全量热数据

5 级(数据源)

MySQL/PostgreSQL(主从)

毫秒级

只承担写操作 + 冷数据查询

源数据、实时强一致数据

核心设计原则

  1. 读请求自上而下:上层缓存命中,直接返回;未命中才查询下层,查询后回写上层缓存
  2. 写请求自下而上先写数据库,再删除 / 更新缓存(绝对不先更缓存);
  3. 热点数据上移:秒杀、首页等极端热点数据,只存在本地 + Redis,不查 DB;
  4. 最终一致性优先:99% 业务无需强一致,用异步同步缓存,兼顾性能与成本。

二、核心抗高并发策略(解决三大缓存问题)

高并发场景下,缓存雪崩、击穿、穿透是最大风险,多级缓存可通过分层兜底彻底解决:

缓存雪崩(大量缓存同时过期 / Redis 宕机)

  • 多级兜底:Redis 宕机→直接读本地缓存 + 数据库,不雪崩;
  • 过期时间随机:给缓存过期时间加随机偏移(如30min + random(5min)),避免集体失效;
  • Redis 高可用:主从 + 哨兵集群,杜绝单节点故障;
  • 热点数据永不过期:后台异步刷新,不设置过期时间。

缓存击穿(热点 Key 过期,流量直接打 DB)

  • 本地缓存永不过期:极端热点数据只存在本地堆缓存,后台异步更新;
  • 互斥锁重建:缓存失效时,用分布式锁(Redlock)保证只有一个线程查 DB;
  • Redis 热点 Key 拆分:把hot_key拆分为hot_key_1~hot_key_10,分散并发压力。

缓存穿透(查询不存在的数据,绕过缓存查 DB)

  • 布隆过滤器:Redis 集成布隆过滤器,拦截不存在的 Key(秒杀、商品查询必备);
  • 空值缓存:查询不存在的数据,缓存空值(短过期时间,如 1 分钟);
  • 参数校验 + 黑名单:拦截非法请求,直接返回。

三、降成本最佳实践(核心!)

多级缓存的成本优化远大于单纯扩容 Redis/DB,这是企业级架构的核心竞争力:

缓存分层淘汰(减少内存占用)

  • 本地缓存:用 Caffeine 的LFU(最不经常使用)淘汰策略,只保留热点数据,避免 OOM;
  • Redis 缓存:设置合理过期时间,冷数据自动淘汰,不占用昂贵的内存资源;
  • 禁用永久缓存:非核心数据绝不设置永不过期,定期清理冷数据。

Redis 成本极致优化(云厂商 Redis 成本最高)

  1. 内存压缩
  1. 数据序列化用Protobuf替代 JSON,体积减少 50%+;
  2. 大 Key 拆分:避免>10KB的 Key,用 Hash/List 结构替代 String;
  1. 读写分离
  1. 读请求走 Redis 从节点,写请求走主节点,主节点只负责写入,降低集群规格;
  1. 淘汰策略
  1. 设置为volatile-lfu:只淘汰带过期时间的冷数据,保留永久热点数据;
  1. 混合存储
  1. Redis 6.0+ 开启磁盘冷数据存储:内存存热数据,磁盘存冷数据,内存成本降低 70%;
  1. 集群选型
  1. 中小流量用主从 + 哨兵,替代高成本的分片集群;
  2. 非核心业务用按量付费云 Redis,核心业务包年包月。

减少回源请求(降低 DB/Redis 压力)

  • CDN 预热:静态资源提前推送到边缘节点,避免回源到服务器;
  • 缓存预热:服务启动时,自动加载热点数据到本地 + Redis;
  • 本地缓存兜底:Redis 故障时,直接用本地缓存响应,不请求 DB。

静态资源全上 CDN(成本降低 90%)

  • 图片、JS、CSS、短视频100% 托管 CDN,CDN 流量费比服务器带宽便宜 10 倍以上;
  • 开启 CDN 缓存压缩、懒加载,进一步降低流量成本。

数据库成本优化

  • 读写分离:读请求走从库,主库只负责写入,避免主库扩容;
  • 分库分表:冷数据归档,只保留热数据在主库;
  • 禁用数据库缓存:多级缓存已兜底,无需开启 DB 缓存,减少 CPU 消耗。

四、生产落地实战(Java 微服务示例)

技术栈

SpringBoot 3.x + Caffeine(本地缓存)+ Redis Cluster + MySQL + 布隆过滤器

核心读写流程(标准规范)

读流程(自上而下,命中返回,未命中回写)
客户端请求 → 1级客户端缓存 → 2级CDN → 3级本地缓存 → 4级Redis → 5级DB 命中:直接返回 未命中:查询下层 → 回写上层所有缓存 → 返回结果
写流程(自下而上,先 DB 后删缓存,绝对不更新)
1. 写入数据库(主库) 2. 异步删除 Redis缓存 3. 广播通知所有服务节点 删除本地缓存 4. 客户端缓存:设置短过期,自动失效

为什么用删除而非更新?并发场景下,更新缓存会产生脏数据,删除是最优解。

核心代码实现

(1)本地缓存配置(Caffeine,生产首选)
@Configuration public class CaffeineConfig { @Bean public Cache<String, Object> caffeineCache() { return Caffeine.newBuilder().maximumSize(10000) // 最大缓存数,防止OOM .expireAfterWrite(30, TimeUnit.MINUTES) // 基础过期时间 .expireAfterAccess(10, TimeUnit.MINUTES) // 空闲过期 .build(); } }
(2)多级缓存工具类
@Service @RequiredArgsConstructor public class MultiLevelCacheService { private final Cache<String, Object> caffeineCache; private final StringRedisTemplate redisTemplate; private final ProductMapper productMapper; private final long REDIS_EXPIRE = 30; private final Random random = new Random(); // 多级缓存读取 public Object get(String key) { // 1. 查本地缓存(最快) Object localCache = caffeineCache.getIfPresent(key); if (localCache != null) return localCache; // 2. 查 RedisString redisValue = redisTemplate.opsForValue().get(key); if (redisValue != null) { caffeineCache.put(key, redisValue); // 回写本地缓存 return redisValue; } // 3. 查数据库(加分布式锁,防止击穿) RLock lock = redissonClient.getLock("lock:" + key); try { lock.lock(5, TimeUnit.SECONDS); // 双重校验,避免并发查库 redisValue = redisTemplate.opsForValue().get(key); if (redisValue != null) return redisValue; // 4. 查询 DBObject dbData = productMapper.selectById(key); if (dbData == null) { // 空值缓存,防穿透 redisTemplate.opsForValue().set(key, "null", 1, TimeUnit.MINUTES); return null; } String data = JSON.toJSONString(dbData); // 5. 回写Redis(随机过期,防雪崩) redisTemplate.opsForValue().set(key, data, REDIS_EXPIRE + random.nextInt(5), TimeUnit.MINUTES); // 6. 回写本地缓存 caffeineCache.put(key, data); return data; } finally { if (lock.isHeldByCurrentThread()) lock.unlock(); } } // 写操作:先DB,后删缓存 @Transactional public void update(String key, Object data) { // 1. 写数据库 productMapper.updateById(data); // 2. 异步删Redis缓存 taskExecutor.execute(() -> redisTemplate.delete(key)); // 3. 广播删本地缓存(MQ实现) rocketMQTemplate.convertAndSend("cache-topic", key); } }
(3)本地缓存一致性(MQ 广播)

服务节点监听 MQ 消息,收到缓存删除通知后,清空本地缓存:

@RocketMQMessageListener(topic = "cache-topic", consumerGroup = "cache-group") @Component public class CacheClearListener implements RocketMQListener<String> { @Autowiredprivate Cache<String, Object> caffeineCache; @Override public void onMessage(String key) { caffeineCache.invalidate(key); // 删除本地缓存 } }

五、缓存一致性方案(多级缓存核心难点)

最终一致性(99% 业务推荐)

  • 方案:先写 DB → 异步删除缓存(MQ/Canal 监听 binlog);
  • 优势:性能极高,无代码侵入,成本低;
  • 适用:商品、订单、内容平台等非强一致业务。

强一致性(金融 / 支付核心场景)

  • 方案:禁用多级缓存,只用 Redis 分布式锁 + 实时更新;
  • 劣势:性能下降,成本升高,仅核心接口使用。

无侵入式缓存同步(Canal)

通过 Canal 监听 MySQL binlog,自动同步数据到 Redis,无需业务代码修改,生产首选


六、高可用保障

  1. Redis 高可用:主从 + 哨兵集群,开启 RDB+AOF 混合持久化;
  2. 熔断降级:用 Sentinel 实现 Redis 宕机时,直接切换为本地缓存 + DB;
  3. 限流防护:接口限流 + 热点 Key 限流,防止流量打垮服务;
  4. 监控告警
  1. 核心指标:缓存命中率(目标 > 95%,低于 90% 优化)、Redis 内存 / CPU、本地缓存大小;
  2. 告警:命中率过低、Redis 连接超限、DB 慢查询。

七、生产避坑指南

  1. 本地缓存不存大数据:避免服务 OOM,单 Key≤1KB;
  2. 杜绝 Redis 大 Key:≥10KB 的 Key 拆分,否则导致网络阻塞;
  3. 不使用固定过期时间:必须加随机偏移,防止雪崩;
  4. 写操作只删不更:绝对不更新缓存,避免并发脏数据;
  5. 客户端 / CDN 不存敏感数据:密码、密钥等禁止缓存;
  6. 冷数据不进 Redis:归档到数据库,节省内存成本。

总结

多级缓存最佳实践核心

  1. 五层架构层层拦截:把请求挡在最上层,最大化抗并发、最小化成本;
  2. 先 DB 后删缓存:保证数据一致性,杜绝脏数据;
  3. 热点上移、冷数据淘汰:本地存极端热点,Redis 存热数据,DB 存源数据;
  4. 成本极致优化:CDN 扛静态、Redis 读写分离、本地缓存兜底,整体成本降低 70%+;
  5. 最终一致性优先:兼顾性能与业务需求,是生产环境的最优解。

这套架构可支撑百万 QPS级高并发,同时将服务器 / 缓存 / 带宽成本降到最低,是互联网公司的标准解决方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询