本地缓存Caffeine/Guava Redis分布式缓存架构搭建一、架构概述架构选型一级缓存本地缓存Caffeine替代 GuavaSpring 官方推荐性能碾压 Guava内存级读写速度纳秒级二级缓存分布式缓存Redis分布式共享缓存解决本地缓存数据不一致问题底层数据源MySQL / 数据库框架SpringBoot 2.x/3.x核心流程读流程请求 → 查 Caffeine 本地缓存 → 命中直接返回 → 未命中查 Redis → 命中回写本地缓存 → 未命中查数据库 → 回写本地 Redis 缓存写流程更新数据库 →删除Redis 缓存 → 发布 Redis 消息 → 所有服务节点删除本地 Caffeine 缓存保证分布式一致性淘汰策略本地缓存设置短过期时间最大容量Redis 设置过期时间内存淘汰核心优势极致性能本地缓存无网络开销扛高并发读降低压力大幅减少 Redis 访问频次避免 Redis 成为瓶颈高可用Redis 宕机时本地缓存可兜底服务二、项目搭建完整可运行核心 Maven 依赖?xml version1.0 encodingUTF-8? dependencies !-- SpringBoot 缓存启动器 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-cache/artifactId /dependency !-- Caffeine 本地缓存替代Guava -- dependency groupIdcom.github.benmanes.caffeine/groupId artifactIdcaffeine/artifactId /dependency !-- Redis 分布式缓存 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- Redis 连接池 -- dependency groupIdorg.apache.commons/groupId artifactIdcommons-pool2/artifactId /dependency !-- Web 工具类 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies配置文件application.yml统一配置 Redis 连接、Caffeine 本地缓存参数yamlspring: # 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: initialCapacity100,maximumSize10000,expireAfterWrite5m # Redis 缓存过期时间分钟 redis-expire: 10 # Redis 发布订阅主题用于同步本地缓存 topic: cache: evict: topic核心配置类1RedisTemplate 序列化配置解决 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 RedisTemplateString, Object redisTemplate(LettuceConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); // String 序列化器key StringRedisSerializer stringSerializer new StringRedisSerializer(); template.setKeySerializer(stringSerializer); template.setHashKeySerializer(stringSerializer); // JSON 序列化器value Jackson2JsonRedisSerializerObject 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; } }2Caffeine 本地缓存实例配置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 CacheString, 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; } }3Redis 发布订阅配置解决分布式缓存一致性核心问题分布式环境下一个服务更新数据后仅删除自己的本地缓存其他服务的本地缓存仍为旧数据 → 通过 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 CacheString, 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 CacheString, Object caffeineCache; private final RedisTemplateString, Object redisTemplate; Value(${cache.redis-expire}) private Integer redisExpire; Value(${cache.topic}) private String cacheTopic; // 读缓存 public T T get(String key, FunctionString, T dbLoader) { // 1. 查一级本地缓存 CaffeineT 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 集群部署本地缓存兜底四、关键注意事项缓存更新策略先更新数据库再删除缓存绝对不要更新缓存避免并发数据不一致过期时间本地缓存过期时间 Redis 过期时间避免本地缓存脏数据本地缓存容量Caffeine 设置maximumSize防止 OOM适用场景读多写少、数据一致性要求不极高的业务商品、配置、用户信息等Guava 兼容若需使用 Guava仅需替换 Caffeine 依赖和配置API 几乎一致五、总结本架构采用Caffeine一级 Redis二级双缓存模式兼顾性能与分布式一致性通过Redis 发布订阅解决分布式环境下本地缓存同步问题内置缓存穿透 / 击穿 / 雪崩防护生产环境直接可用核心原则读缓存写删缓存保证数据最终一致性多级缓存一致性、更新策略、失效同步方案在分布式系统中多级缓存本地缓存 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 缓存 → 同步失效本地缓存。为什么是「删除缓存」而不是「更新缓存」避免无效写数据频繁更新时更新缓存会造成大量无用 IO避免脏数据更新缓存可能导致并发读写的数据错乱。为什么是「先 DB 后缓存」如果先删缓存再更新 DB会出现致命脏数据读请求→缓存未命中→查DB旧数据 → 写请求→更新DB → 读请求→把旧数据写入缓存最终缓存永远是旧数据必须先更新 DB再删缓存。写穿模式Write Through核心缓存与数据库同步写入强一致写操作同时更新 DB 和 Redis业务无感知缺点性能差写请求延迟高极少使用。写回模式Write Back核心先写缓存异步批量刷库写操作只更新缓存后台线程异步合并更新数据库优点写性能极致缺点存在数据丢失风险一致性极差仅用于日志等非核心数据。三、多级缓存一致性核心问题Redis 是分布式共享缓存删除一次即可全局生效本地缓存是应用节点私有这是一致性的最大痛点数据更新后仅删除 Redis未失效本地缓存 → 其他节点读取旧本地缓存并发读写场景短暂的缓存脏数据缓存失效、消息丢失导致的最终不一致。四、多级缓存失效同步方案核心解决方案按简单→可靠→无侵入排序覆盖所有业务场景方案 1短过期时间兜底方案最简单实现给本地缓存设置极短 TTL如 3~5 秒Redis 设置较长过期时间如 30 分钟。流程数据更新→更新 DB→删除 Redis→不主动删本地缓存→本地缓存到期自动失效。优缺点✅ 零开发成本无需中间件❌ 一致性差最多存在几秒脏数据适用对一致性要求极低的非核心数据如统计数据、首页推荐。方案 2消息通知同步生产主流实时性高✅核心通过消息队列 / 发布订阅通知所有应用节点删除本地缓存这是解决本地缓存同步的标准方案分两种实现实现 1Redis 发布 / 订阅轻量无额外中间件数据更新 → 更新 DB → 删除 Redis 缓存向 Redis 指定 Channel 发送缓存失效消息key / 标识所有应用节点订阅该 Channel收到消息后删除本地 L1 缓存。实现 2专业 MQRabbitMQ/Kafka高可靠流程同上仅将 Redis Pub/Sub 替换为 MQ优势消息持久化、不丢失、支持重试适用核心业务数据。完整流程写请求 → 更新DB → 删除Redis → 发送失效消息 → 所有节点消费消息 → 删除本地缓存优缺点✅ 实时性高、一致性好、架构简单❌ Redis Pub/Sub 无持久化消息可能丢失MQ 可解决适用绝大多数微服务业务。方案 3Canal 监听 Binlog无侵入终极方案✅核心不侵入业务代码通过数据库 Binlog 同步缓存失效适合多个服务写入同一张表、无法在业务代码中加缓存删除逻辑的场景。流程DB 执行更新 / 插入→生成 BinlogCanal 组件监听 Binlog解析数据变更Canal 推送变更消息→删除 Redis 缓存→通知所有节点删除本地缓存业务代码完全不关心缓存零耦合。优缺点✅ 业务无侵入、强一致、多写入口兼容❌ 需要部署 Canal 服务运维成本高适用核心交易数据、多服务共享表。方案 4延迟双删解决并发脏数据针对并发读写的短暂脏数据问题在方案 2 的基础上增加兜底更新DB → 删除Redis → 延迟200ms → 再次删除Redis → 发送消息删本地缓存延迟时间略大于业务读请求的平均耗时彻底清理并发写入的旧缓存。方案 5版本号机制强一致兜底给缓存 key 增加版本号彻底杜绝并发脏数据数据更新时DB 版本号 1删除缓存时携带新版本号读缓存时校验版本号不一致则重新查库加载。五、方案对比与选型建议方案一致性复杂度成本适用场景本地缓存短过期差极低低非核心、读多写少数据Redis Pub/Sub 通知良低低普通业务、非核心数据MQ 通知同步优中中核心业务、要求高一致性Canal Binlog 同步极优高高多写入口、强一致核心数据延迟双删 版本号最优中中并发极高、零脏数据要求六、生产级最佳实践必看组合方案万金油本地缓存短 TTL 兜底Redis Pub/Sub/MQ 实时同步延迟双删平衡一致性与性能。缓存禁用自动刷新本地缓存只做主动删除不做定时刷新避免无效数据加载。防护机制缓存穿透查询不存在的数据缓存空值缓存雪崩Redis 过期时间加随机偏移本地缓存错开 TTL消息重复消费消费端做幂等校验。监控监控缓存命中率、本地缓存失效成功率、消息消费失败率。七、总结多级缓存架构本地 L1 Redis L2 DB读流程自上而下写流程核心是先 DB 后删缓存唯一推荐写策略旁路缓存模式Cache Aside本地缓存同步生产用MQ/Redis 发布订阅核心数据用Canal并发一致性用延迟双删 版本号兜底杜绝脏数据。这套方案是互联网公司通用的多级缓存一致性解决方案兼顾性能、一致性与开发成本。多级缓存抗高并发、降成本架构最佳实践多级缓存的核心价值越靠近用户的缓存层级速度越快、成本越低、抗并发能力越强。通过客户端缓存 → 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主从毫秒级高只承担写操作 冷数据查询源数据、实时强一致数据核心设计原则读请求自上而下上层缓存命中直接返回未命中才查询下层查询后回写上层缓存写请求自下而上先写数据库再删除 / 更新缓存绝对不先更缓存热点数据上移秒杀、首页等极端热点数据只存在本地 Redis不查 DB最终一致性优先99% 业务无需强一致用异步同步缓存兼顾性能与成本。二、核心抗高并发策略解决三大缓存问题高并发场景下缓存雪崩、击穿、穿透是最大风险多级缓存可通过分层兜底彻底解决缓存雪崩大量缓存同时过期 / Redis 宕机多级兜底Redis 宕机→直接读本地缓存 数据库不雪崩过期时间随机给缓存过期时间加随机偏移如30min random(5min)避免集体失效Redis 高可用主从 哨兵集群杜绝单节点故障热点数据永不过期后台异步刷新不设置过期时间。缓存击穿热点 Key 过期流量直接打 DB本地缓存永不过期极端热点数据只存在本地堆缓存后台异步更新互斥锁重建缓存失效时用分布式锁Redlock保证只有一个线程查 DBRedis 热点 Key 拆分把hot_key拆分为hot_key_1~hot_key_10分散并发压力。缓存穿透查询不存在的数据绕过缓存查 DB布隆过滤器Redis 集成布隆过滤器拦截不存在的 Key秒杀、商品查询必备空值缓存查询不存在的数据缓存空值短过期时间如 1 分钟参数校验 黑名单拦截非法请求直接返回。三、降成本最佳实践核心多级缓存的成本优化远大于单纯扩容 Redis/DB这是企业级架构的核心竞争力缓存分层淘汰减少内存占用本地缓存用 Caffeine 的LFU最不经常使用淘汰策略只保留热点数据避免 OOMRedis 缓存设置合理过期时间冷数据自动淘汰不占用昂贵的内存资源禁用永久缓存非核心数据绝不设置永不过期定期清理冷数据。Redis 成本极致优化云厂商 Redis 成本最高内存压缩数据序列化用Protobuf替代 JSON体积减少 50%大 Key 拆分避免10KB的 Key用 Hash/List 结构替代 String读写分离读请求走 Redis 从节点写请求走主节点主节点只负责写入降低集群规格淘汰策略设置为volatile-lfu只淘汰带过期时间的冷数据保留永久热点数据混合存储Redis 6.0 开启磁盘冷数据存储内存存热数据磁盘存冷数据内存成本降低 70%集群选型中小流量用主从 哨兵替代高成本的分片集群非核心业务用按量付费云 Redis核心业务包年包月。减少回源请求降低 DB/Redis 压力CDN 预热静态资源提前推送到边缘节点避免回源到服务器缓存预热服务启动时自动加载热点数据到本地 Redis本地缓存兜底Redis 故障时直接用本地缓存响应不请求 DB。静态资源全上 CDN成本降低 90%图片、JS、CSS、短视频100% 托管 CDNCDN 流量费比服务器带宽便宜 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 CacheString, 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 CacheString, 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 RocketMQListenerString { Autowiredprivate CacheString, Object caffeineCache; Override public void onMessage(String key) { caffeineCache.invalidate(key); // 删除本地缓存 } }五、缓存一致性方案多级缓存核心难点最终一致性99% 业务推荐方案先写 DB → 异步删除缓存MQ/Canal 监听 binlog优势性能极高无代码侵入成本低适用商品、订单、内容平台等非强一致业务。强一致性金融 / 支付核心场景方案禁用多级缓存只用 Redis 分布式锁 实时更新劣势性能下降成本升高仅核心接口使用。无侵入式缓存同步Canal通过 Canal 监听 MySQL binlog自动同步数据到 Redis无需业务代码修改生产首选。六、高可用保障Redis 高可用主从 哨兵集群开启 RDBAOF 混合持久化熔断降级用 Sentinel 实现 Redis 宕机时直接切换为本地缓存 DB限流防护接口限流 热点 Key 限流防止流量打垮服务监控告警核心指标缓存命中率目标 95%低于 90% 优化、Redis 内存 / CPU、本地缓存大小告警命中率过低、Redis 连接超限、DB 慢查询。七、生产避坑指南本地缓存不存大数据避免服务 OOM单 Key≤1KB杜绝 Redis 大 Key≥10KB 的 Key 拆分否则导致网络阻塞不使用固定过期时间必须加随机偏移防止雪崩写操作只删不更绝对不更新缓存避免并发脏数据客户端 / CDN 不存敏感数据密码、密钥等禁止缓存冷数据不进 Redis归档到数据库节省内存成本。总结多级缓存最佳实践核心五层架构层层拦截把请求挡在最上层最大化抗并发、最小化成本先 DB 后删缓存保证数据一致性杜绝脏数据热点上移、冷数据淘汰本地存极端热点Redis 存热数据DB 存源数据成本极致优化CDN 扛静态、Redis 读写分离、本地缓存兜底整体成本降低 70%最终一致性优先兼顾性能与业务需求是生产环境的最优解。这套架构可支撑百万 QPS级高并发同时将服务器 / 缓存 / 带宽成本降到最低是互联网公司的标准解决方案。
【Redis分布式缓存实战】第19章 多级缓存架构设计实战
发布时间:2026/6/9 3:52:10
本地缓存Caffeine/Guava Redis分布式缓存架构搭建一、架构概述架构选型一级缓存本地缓存Caffeine替代 GuavaSpring 官方推荐性能碾压 Guava内存级读写速度纳秒级二级缓存分布式缓存Redis分布式共享缓存解决本地缓存数据不一致问题底层数据源MySQL / 数据库框架SpringBoot 2.x/3.x核心流程读流程请求 → 查 Caffeine 本地缓存 → 命中直接返回 → 未命中查 Redis → 命中回写本地缓存 → 未命中查数据库 → 回写本地 Redis 缓存写流程更新数据库 →删除Redis 缓存 → 发布 Redis 消息 → 所有服务节点删除本地 Caffeine 缓存保证分布式一致性淘汰策略本地缓存设置短过期时间最大容量Redis 设置过期时间内存淘汰核心优势极致性能本地缓存无网络开销扛高并发读降低压力大幅减少 Redis 访问频次避免 Redis 成为瓶颈高可用Redis 宕机时本地缓存可兜底服务二、项目搭建完整可运行核心 Maven 依赖?xml version1.0 encodingUTF-8? dependencies !-- SpringBoot 缓存启动器 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-cache/artifactId /dependency !-- Caffeine 本地缓存替代Guava -- dependency groupIdcom.github.benmanes.caffeine/groupId artifactIdcaffeine/artifactId /dependency !-- Redis 分布式缓存 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- Redis 连接池 -- dependency groupIdorg.apache.commons/groupId artifactIdcommons-pool2/artifactId /dependency !-- Web 工具类 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies配置文件application.yml统一配置 Redis 连接、Caffeine 本地缓存参数yamlspring: # 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: initialCapacity100,maximumSize10000,expireAfterWrite5m # Redis 缓存过期时间分钟 redis-expire: 10 # Redis 发布订阅主题用于同步本地缓存 topic: cache: evict: topic核心配置类1RedisTemplate 序列化配置解决 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 RedisTemplateString, Object redisTemplate(LettuceConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); // String 序列化器key StringRedisSerializer stringSerializer new StringRedisSerializer(); template.setKeySerializer(stringSerializer); template.setHashKeySerializer(stringSerializer); // JSON 序列化器value Jackson2JsonRedisSerializerObject 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; } }2Caffeine 本地缓存实例配置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 CacheString, 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; } }3Redis 发布订阅配置解决分布式缓存一致性核心问题分布式环境下一个服务更新数据后仅删除自己的本地缓存其他服务的本地缓存仍为旧数据 → 通过 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 CacheString, 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 CacheString, Object caffeineCache; private final RedisTemplateString, Object redisTemplate; Value(${cache.redis-expire}) private Integer redisExpire; Value(${cache.topic}) private String cacheTopic; // 读缓存 public T T get(String key, FunctionString, T dbLoader) { // 1. 查一级本地缓存 CaffeineT 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 集群部署本地缓存兜底四、关键注意事项缓存更新策略先更新数据库再删除缓存绝对不要更新缓存避免并发数据不一致过期时间本地缓存过期时间 Redis 过期时间避免本地缓存脏数据本地缓存容量Caffeine 设置maximumSize防止 OOM适用场景读多写少、数据一致性要求不极高的业务商品、配置、用户信息等Guava 兼容若需使用 Guava仅需替换 Caffeine 依赖和配置API 几乎一致五、总结本架构采用Caffeine一级 Redis二级双缓存模式兼顾性能与分布式一致性通过Redis 发布订阅解决分布式环境下本地缓存同步问题内置缓存穿透 / 击穿 / 雪崩防护生产环境直接可用核心原则读缓存写删缓存保证数据最终一致性多级缓存一致性、更新策略、失效同步方案在分布式系统中多级缓存本地缓存 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 缓存 → 同步失效本地缓存。为什么是「删除缓存」而不是「更新缓存」避免无效写数据频繁更新时更新缓存会造成大量无用 IO避免脏数据更新缓存可能导致并发读写的数据错乱。为什么是「先 DB 后缓存」如果先删缓存再更新 DB会出现致命脏数据读请求→缓存未命中→查DB旧数据 → 写请求→更新DB → 读请求→把旧数据写入缓存最终缓存永远是旧数据必须先更新 DB再删缓存。写穿模式Write Through核心缓存与数据库同步写入强一致写操作同时更新 DB 和 Redis业务无感知缺点性能差写请求延迟高极少使用。写回模式Write Back核心先写缓存异步批量刷库写操作只更新缓存后台线程异步合并更新数据库优点写性能极致缺点存在数据丢失风险一致性极差仅用于日志等非核心数据。三、多级缓存一致性核心问题Redis 是分布式共享缓存删除一次即可全局生效本地缓存是应用节点私有这是一致性的最大痛点数据更新后仅删除 Redis未失效本地缓存 → 其他节点读取旧本地缓存并发读写场景短暂的缓存脏数据缓存失效、消息丢失导致的最终不一致。四、多级缓存失效同步方案核心解决方案按简单→可靠→无侵入排序覆盖所有业务场景方案 1短过期时间兜底方案最简单实现给本地缓存设置极短 TTL如 3~5 秒Redis 设置较长过期时间如 30 分钟。流程数据更新→更新 DB→删除 Redis→不主动删本地缓存→本地缓存到期自动失效。优缺点✅ 零开发成本无需中间件❌ 一致性差最多存在几秒脏数据适用对一致性要求极低的非核心数据如统计数据、首页推荐。方案 2消息通知同步生产主流实时性高✅核心通过消息队列 / 发布订阅通知所有应用节点删除本地缓存这是解决本地缓存同步的标准方案分两种实现实现 1Redis 发布 / 订阅轻量无额外中间件数据更新 → 更新 DB → 删除 Redis 缓存向 Redis 指定 Channel 发送缓存失效消息key / 标识所有应用节点订阅该 Channel收到消息后删除本地 L1 缓存。实现 2专业 MQRabbitMQ/Kafka高可靠流程同上仅将 Redis Pub/Sub 替换为 MQ优势消息持久化、不丢失、支持重试适用核心业务数据。完整流程写请求 → 更新DB → 删除Redis → 发送失效消息 → 所有节点消费消息 → 删除本地缓存优缺点✅ 实时性高、一致性好、架构简单❌ Redis Pub/Sub 无持久化消息可能丢失MQ 可解决适用绝大多数微服务业务。方案 3Canal 监听 Binlog无侵入终极方案✅核心不侵入业务代码通过数据库 Binlog 同步缓存失效适合多个服务写入同一张表、无法在业务代码中加缓存删除逻辑的场景。流程DB 执行更新 / 插入→生成 BinlogCanal 组件监听 Binlog解析数据变更Canal 推送变更消息→删除 Redis 缓存→通知所有节点删除本地缓存业务代码完全不关心缓存零耦合。优缺点✅ 业务无侵入、强一致、多写入口兼容❌ 需要部署 Canal 服务运维成本高适用核心交易数据、多服务共享表。方案 4延迟双删解决并发脏数据针对并发读写的短暂脏数据问题在方案 2 的基础上增加兜底更新DB → 删除Redis → 延迟200ms → 再次删除Redis → 发送消息删本地缓存延迟时间略大于业务读请求的平均耗时彻底清理并发写入的旧缓存。方案 5版本号机制强一致兜底给缓存 key 增加版本号彻底杜绝并发脏数据数据更新时DB 版本号 1删除缓存时携带新版本号读缓存时校验版本号不一致则重新查库加载。五、方案对比与选型建议方案一致性复杂度成本适用场景本地缓存短过期差极低低非核心、读多写少数据Redis Pub/Sub 通知良低低普通业务、非核心数据MQ 通知同步优中中核心业务、要求高一致性Canal Binlog 同步极优高高多写入口、强一致核心数据延迟双删 版本号最优中中并发极高、零脏数据要求六、生产级最佳实践必看组合方案万金油本地缓存短 TTL 兜底Redis Pub/Sub/MQ 实时同步延迟双删平衡一致性与性能。缓存禁用自动刷新本地缓存只做主动删除不做定时刷新避免无效数据加载。防护机制缓存穿透查询不存在的数据缓存空值缓存雪崩Redis 过期时间加随机偏移本地缓存错开 TTL消息重复消费消费端做幂等校验。监控监控缓存命中率、本地缓存失效成功率、消息消费失败率。七、总结多级缓存架构本地 L1 Redis L2 DB读流程自上而下写流程核心是先 DB 后删缓存唯一推荐写策略旁路缓存模式Cache Aside本地缓存同步生产用MQ/Redis 发布订阅核心数据用Canal并发一致性用延迟双删 版本号兜底杜绝脏数据。这套方案是互联网公司通用的多级缓存一致性解决方案兼顾性能、一致性与开发成本。多级缓存抗高并发、降成本架构最佳实践多级缓存的核心价值越靠近用户的缓存层级速度越快、成本越低、抗并发能力越强。通过客户端缓存 → 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主从毫秒级高只承担写操作 冷数据查询源数据、实时强一致数据核心设计原则读请求自上而下上层缓存命中直接返回未命中才查询下层查询后回写上层缓存写请求自下而上先写数据库再删除 / 更新缓存绝对不先更缓存热点数据上移秒杀、首页等极端热点数据只存在本地 Redis不查 DB最终一致性优先99% 业务无需强一致用异步同步缓存兼顾性能与成本。二、核心抗高并发策略解决三大缓存问题高并发场景下缓存雪崩、击穿、穿透是最大风险多级缓存可通过分层兜底彻底解决缓存雪崩大量缓存同时过期 / Redis 宕机多级兜底Redis 宕机→直接读本地缓存 数据库不雪崩过期时间随机给缓存过期时间加随机偏移如30min random(5min)避免集体失效Redis 高可用主从 哨兵集群杜绝单节点故障热点数据永不过期后台异步刷新不设置过期时间。缓存击穿热点 Key 过期流量直接打 DB本地缓存永不过期极端热点数据只存在本地堆缓存后台异步更新互斥锁重建缓存失效时用分布式锁Redlock保证只有一个线程查 DBRedis 热点 Key 拆分把hot_key拆分为hot_key_1~hot_key_10分散并发压力。缓存穿透查询不存在的数据绕过缓存查 DB布隆过滤器Redis 集成布隆过滤器拦截不存在的 Key秒杀、商品查询必备空值缓存查询不存在的数据缓存空值短过期时间如 1 分钟参数校验 黑名单拦截非法请求直接返回。三、降成本最佳实践核心多级缓存的成本优化远大于单纯扩容 Redis/DB这是企业级架构的核心竞争力缓存分层淘汰减少内存占用本地缓存用 Caffeine 的LFU最不经常使用淘汰策略只保留热点数据避免 OOMRedis 缓存设置合理过期时间冷数据自动淘汰不占用昂贵的内存资源禁用永久缓存非核心数据绝不设置永不过期定期清理冷数据。Redis 成本极致优化云厂商 Redis 成本最高内存压缩数据序列化用Protobuf替代 JSON体积减少 50%大 Key 拆分避免10KB的 Key用 Hash/List 结构替代 String读写分离读请求走 Redis 从节点写请求走主节点主节点只负责写入降低集群规格淘汰策略设置为volatile-lfu只淘汰带过期时间的冷数据保留永久热点数据混合存储Redis 6.0 开启磁盘冷数据存储内存存热数据磁盘存冷数据内存成本降低 70%集群选型中小流量用主从 哨兵替代高成本的分片集群非核心业务用按量付费云 Redis核心业务包年包月。减少回源请求降低 DB/Redis 压力CDN 预热静态资源提前推送到边缘节点避免回源到服务器缓存预热服务启动时自动加载热点数据到本地 Redis本地缓存兜底Redis 故障时直接用本地缓存响应不请求 DB。静态资源全上 CDN成本降低 90%图片、JS、CSS、短视频100% 托管 CDNCDN 流量费比服务器带宽便宜 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 CacheString, 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 CacheString, 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 RocketMQListenerString { Autowiredprivate CacheString, Object caffeineCache; Override public void onMessage(String key) { caffeineCache.invalidate(key); // 删除本地缓存 } }五、缓存一致性方案多级缓存核心难点最终一致性99% 业务推荐方案先写 DB → 异步删除缓存MQ/Canal 监听 binlog优势性能极高无代码侵入成本低适用商品、订单、内容平台等非强一致业务。强一致性金融 / 支付核心场景方案禁用多级缓存只用 Redis 分布式锁 实时更新劣势性能下降成本升高仅核心接口使用。无侵入式缓存同步Canal通过 Canal 监听 MySQL binlog自动同步数据到 Redis无需业务代码修改生产首选。六、高可用保障Redis 高可用主从 哨兵集群开启 RDBAOF 混合持久化熔断降级用 Sentinel 实现 Redis 宕机时直接切换为本地缓存 DB限流防护接口限流 热点 Key 限流防止流量打垮服务监控告警核心指标缓存命中率目标 95%低于 90% 优化、Redis 内存 / CPU、本地缓存大小告警命中率过低、Redis 连接超限、DB 慢查询。七、生产避坑指南本地缓存不存大数据避免服务 OOM单 Key≤1KB杜绝 Redis 大 Key≥10KB 的 Key 拆分否则导致网络阻塞不使用固定过期时间必须加随机偏移防止雪崩写操作只删不更绝对不更新缓存避免并发脏数据客户端 / CDN 不存敏感数据密码、密钥等禁止缓存冷数据不进 Redis归档到数据库节省内存成本。总结多级缓存最佳实践核心五层架构层层拦截把请求挡在最上层最大化抗并发、最小化成本先 DB 后删缓存保证数据一致性杜绝脏数据热点上移、冷数据淘汰本地存极端热点Redis 存热数据DB 存源数据成本极致优化CDN 扛静态、Redis 读写分离、本地缓存兜底整体成本降低 70%最终一致性优先兼顾性能与业务需求是生产环境的最优解。这套架构可支撑百万 QPS级高并发同时将服务器 / 缓存 / 带宽成本降到最低是互联网公司的标准解决方案。