
你用了Redis,网站快了,但偶尔会慢一下。你怀疑是网络延迟在作怪。你听说了本地缓存(Caffeine、Guava),它存在于应用内存中,访问速度是Redis的几十倍。但你也在犹豫:用了本地缓存,多台服务器之间的数据一致性问题怎么解决?
缓存不是选一个工具就完事了。本地缓存和分布式缓存各有所长,组合使用才是大多数系统的最终形态。
本地缓存:快,但只能自己用
本地缓存存储在应用程序所在的内存中。访问本地缓存相当于从当前进程的内存读取数据,不经过网络,不存在序列化/反序列化开销,延迟通常为微秒级。
适用场景:
- 单机应用:只有一个实例在运行,没有跨实例数据共享的需求
- 读多写少且更新频率极低的数据:如国家/省份列表、系统配置项、国际区号等,这些数据极少变化,适合加载到本地缓存中
- 对延迟极度敏感:Redis的网络延迟(毫秒级)已经是不可接受的额外开销,本地缓存(微秒级)是唯一选择
本地缓存的限制在于它无法跨实例共享数据。如果应用部署了多个实例,每个实例都有各自的本地缓存,同一个数据在不同实例之间可能存在不同的值。多实例部署时,需要确保缓存数据在实例间保持一致,但这不是本地缓存能自然做到的,需要额外的机制来管理。
分布式缓存:共享,但有网络开销
Redis、Memcached是典型的分布式缓存。它们独立于应用运行,提供统一的数据存储和访问接口。所有应用实例访问同一个数据源,天然保证数据一致性。
分布式缓存的核心优势在于数据共享,而不是速度。单次访问延迟(毫秒级)高于本地缓存,但能解决跨实例缓存一致性问题。当你有多个应用实例时,使用分布式缓存可能是唯一的选择。
适用场景:
- 多实例部署:多个应用实例需要访问同一份缓存数据,分布式缓存是唯一选择
- 缓存数据量大:本地缓存受限于应用进程的内存上限,分布式缓存可以独立扩展
- 需要跨服务共享缓存数据:不同的微服务需要访问同一份数据
多级缓存:把两者的优势组合起来
典型的多级缓存架构:L1是本地缓存(Caffeine/Guava),L2是分布式缓存(Redis),L3是数据库。读取顺序:本地缓存 → 分布式缓存 → 数据库。写入顺序:数据库 → 删除本地缓存 → 更新分布式缓存。
多级缓存解决了几个关键问题:每次缓存访问先查本地缓存,命中率高时直接返回,避免了大部分对Redis的网络请求;Redis作为L2缓存提供了跨实例数据共享能力,同时避免直接访问数据库;本地缓存未命中时,从Redis获取数据并存入本地缓存,后续请求可以直接命中本地。
多级缓存的代价在于实现复杂度增加。需要处理本地缓存与分布式缓存之间的数据同步问题,需要设置合理的过期时间,避免脏数据问题。缓存更新的策略也需要注意:更新数据库后,先删除本地缓存,再更新分布式缓存,确保后续请求能拿到最新数据。
缓存策略的选型判断
| 判断维度 | 选择本地缓存 | 选择分布式缓存 |
|---|---|---|
| 应用实例数 | 单实例 | 多实例 |
| 数据量 | 小(<100MB) | 大(>100MB) |
| 一致性要求 | 允许短暂不一致 | 必须强一致 |
| 访问延迟要求 | 微秒级 | 毫秒级可接受 |
| 数据变更频率 | 极少变更 | 频繁变更 |
如果应用是单实例部署,本地缓存是值得优先考虑的选项,配置简单且速度快。当应用扩展为多实例时,需要引入分布式缓存来保证数据一致性,同时可以保留本地缓存作为L1加速。
一个真实案例
一个用户信息查询服务,日请求量约500万次。早期使用Redis作为唯一缓存,平均响应时间约12ms。Redis请求量大时网络开销成为主要瓶颈,响应时间经常波动到30ms以上。
引入Caffeine本地缓存作为L1后,热点用户信息的访问命中本地缓存(命中率约85%),响应时间降至0.5ms。Redis请求量减少80%,响应时间稳定在1ms以内。本地缓存的过期时间设为5分钟,通过Redis的发布/订阅机制在数据更新时通知所有实例清除对应本地缓存条目。
最后一句
本地缓存快但无法跨实例共享,分布式缓存共享但速度慢于本地缓存。多级缓存把两者的优势结合起来,代价是实现复杂度增加。最终选择取决于你的业务场景:单实例优先考虑本地缓存,多实例分布式缓存是必要的,数据量大且要求一致性高时多级缓存是合理的架构选择。缓存的本质是空间换时间,多级缓存是把空间分配得更精细。




