2020-09-19 00:24:19
服务端应用多级缓存架构方案通过结合本地缓存与分布式缓存(如Redis集群),优先利用本地缓存提升访问速度,分布式缓存作为后备存储,并通过发布订阅机制实现缓存同步,解决高并发场景下的性能瓶颈与数据一致性问题。 以下是具体方案:
一、场景与挑战在高并发场景(如20万QPS甚至更高)下,单一分布式缓存(如Redis集群)可能面临性能瓶颈,尤其是缓存热点(热Key)问题。例如,10万流量集中访问同一个Key时,单台Redis实例的CPU可能被打满,导致服务不可用。此时,单纯增加Redis集群节点数量无法解决热点问题,需引入多级缓存架构。

核心逻辑:请求优先访问本地缓存,未命中时再访问分布式缓存,并异步更新本地缓存。
本地缓存层
作用:存储热点数据,避免直接访问分布式缓存,减少网络开销与单点压力。
特点:访问速度快(内存存储)、可分散请求压力(多副本)、但容量有限(占用JVM内存)。
适用场景:读多写少、对数据一致性要求不严格的业务(如商品详情、配置信息)。
分布式缓存层(Redis集群)
作用:作为后备存储,提供持久化与高可用能力,支持全局数据共享。
部署建议:采用主从架构(如6主6从),主节点处理读写,从节点作为备份。通过扩展节点数量提升并发能力(如单节点抗3万QPS,100万QPS需约34台节点)。
本地缓存是解决热Key问题的关键手段,具体策略如下:
多副本分散压力将热Key的缓存副本复制到多个应用实例的本地缓存中,请求通过负载均衡分散到不同实例,避免单台Redis被击穿。例如,10万热Key请求被10个实例分担,每台仅处理1万请求。
本地缓存与分布式缓存协同
请求优先查询本地缓存,命中则直接返回;
未命中时,从Redis集群拉取数据,并异步写入本地缓存(如设置短过期时间)。
目标:确保本地缓存与分布式缓存的数据一致性,避免脏数据。采用“发布订阅+定时任务”双保险模式:
流程:
运营后台更新数据时,先写入Redis集群,再通过PUBLISH命令发布消息到指定频道;
应用集群订阅该频道,收到消息后删除本地缓存中对应的Key;
后续请求触发本地缓存未命中,重新从Redis加载数据。
优势:异步通知、低延迟、支持集群化部署。
注意事项:
Pub/Sub消息不持久化,需避免消息丢失(如消费者离线时);
避免传递大Value,防止Redis缓冲区溢出。

容量控制
本地缓存占用JVM内存,需评估数据量与内存占用(如单Key不超过1MB,总缓存不超过JVM堆内存的30%)。
避免存储大数据或频繁更新的数据(如用户会话)。
数据一致性
业务需容忍短暂不一致(如商品价格更新后,本地缓存可能延迟1-5秒生效)。
对一致性要求高的场景(如支付金额),需直接查询Redis或数据库。
缓存更新策略
主动更新:通过Pub/Sub或消息队列推送变更。
被动更新:设置缓存过期时间(TTL),到期后重新加载。
混合策略:对核心数据采用主动更新+短TTL,非核心数据仅用TTL。
并发控制
本地缓存失效时,加锁(如synchronized)避免并发更新导致多次查询Redis。
示例代码:
public String getData(String key) { String value = localCache.get(key); if (value == null) { synchronized (key.intern()) { value = localCache.get(key); // 双重检查 if (value == null) { value = redis.get(key); // 从Redis加载 localCache.put(key, value); } } } return value;}避免缓存污染
RPC调用时,通过深拷贝防止本地缓存被意外修改(如返回对象的字段被外部代码更改)。
示例:
public User getUser(Long userId) { String userJson = localCache.get(userId.toString()); if (userJson == null) { User user = rpcClient.getUser(userId); // RPC调用 localCache.put(userId.toString(), JSON.toJSONString(user)); // 存储JSON字符串 return user; } return JSON.parseObject(userJson, User.class); // 深拷贝解析}启动时初始化
应用重启后,本地缓存为空,需在启动时预加载关键数据(如通过定时任务或启动脚本加载Redis数据到本地)。
优势:
高性能:本地缓存访问延迟低于1ms,Redis集群延迟约1-5ms。
高可用:Redis主从架构+本地缓存兜底,避免单点故障。
弹性扩展:通过增加Redis节点或应用实例应对流量增长。
适用场景:
读多写少、数据热点集中的业务(如电商商品详情、社交媒体动态)。
对延迟敏感的服务(如推荐系统、广告投放)。
不适用场景:
数据频繁更新且一致性要求极高(如金融交易)。
数据量极大(如TB级)且无法分片的场景。