【原创 架构设计】多级缓存的应用、常见问题与解决方式
1. 简介
多级缓存是一种常见的性能优化手段,对于多级缓存一般意义上的理解具体主要实现其实指的就是本地缓存
和分布式缓存
。
本地缓存一般采用Caffeine或者Guava Cache来进行实现,而分布式缓存一般采用Redis来进行实现。
2. 业务流程
业务线程先在本地缓存中查询缓存数据,如果获取不到,则从分布式缓存中获取,并将结果存放到本地缓存中,然后返回给客户端。
本地缓存中数据会设置一定的有效期,在数据过期之后,将重复执行此操作,从分布式缓存中获取数据,如下图所示:
3. 优缺点与解决方案
本地缓存和分布式缓存本质上的作用都是为了提高程序的性能,减少对后端的数据存储资源的访问次数,转载请注明原文地址。
- 本地缓存
优点:速度最快,直接存储在业务进程内,方便使用和管理。
缺点:多个节点实例之间的数据不一致,无持久化应用重启后就丢失,缓存容量受限于单进程内存限制,一般比较有限。 - 分布式缓存
优点:支持多个实例共享缓存数据,缓存容量更大,可扩缩容。
缺点:速度相对来讲不如本地缓存,需要考虑故障恢复和一致性问题。
本地缓存同时也是分布式缓存中热key
的一种有效的解决手段,后面考虑写一篇详细介绍,感兴趣的朋友可以关注下。
3.1. 如何保证本地缓存的一致性
- 给缓存数据加一个版本号
当某一个节点的数据发生变更之后,更新这条数据的版本号,并同步到数据库中,然后返回给客户端。客户端下次再来请求时携带这条数据的版本号,若请求到一个未更新本地缓存的节点上,发现参数中携带的版本号比自己的本地缓存中的版本号新,那么他会从共享存储中重新加载这条缓存,图示如下:
- 缓存数据变更通知
这个方法就很直接了,若节点修改了缓存的数据,那么需要通知其他节点去重新加载缓存数据,来保证多个节点之间的缓存数据一致性。常见的方式有:
1、 修改配置中心配置
:配置中心的配置文件发生变更时会通知所有节点,节点收到更新配置的消息之后重新加载缓存。
2、通过MQ广播消息
:与修改配置中心的配置类似,当节点修改了缓存数据的时候,发送MQ消息,其他节点监听到这条消息之后更新缓存。
- 最终一致
上面的两种方式实时性会好一些,若只需要最终一致性那就比较简单了,直接使用本地缓存的自动失效
或者自动更新
功能。
// 访问后5秒过期---自动失效
Cache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.SECONDS).build();
// 写入后5秒过期,重新加载缓存---自动更新
Cache<String, String> cache = Caffeine.newBuilder().refreshAfterWrite(5, TimeUnit.SECONDS).build(new CacheLoader<String, String>() {
@Override
public @Nullable String load(String s) throws Exception {
// 查询数据库或分布式缓存重新获取缓存值
return "";
}
});
3.2. 本地缓存的适用场景
首先需要提到的一点:程序员做的一切都要以业务为目标,技术只是实现业务的工具。
使用本地缓存前,需要从业务上评估以下两点:
- 评估数据变化的频率
频繁变化的数据,是不适合放入本地缓存中去的,只有不会频繁变化的数据,才适合放到本地缓存中去。-----例如秒杀的库存数据,没人用本地缓存吧 ^ - ^。
- 评估业务上能否接受数据不一致,以及能接受不一致的时长
如果业务能够接受一定时长的不一致,可以根据其能接受的时长做缓存过期的时间设置,提前于最大不能接受的时长对缓存进行过期刷新处理。
4. 多级缓存扩展
完整的多级缓存不仅仅包含本地缓存与分布式缓存,还有一些其他的手段如:客户端缓存、CDN缓存、Nginx服务器缓存等。
- 客户端缓存
根据业务需求,可以将一些不会变更的数据直接缓存到客户端,以此来减少请求服务器的次数。如秒杀的开始时间等,在一次请求服务器之后就可以进行本地计算到计时了,无需多次请求。
- CDN缓存
即内容分发网络,一般的前端js、css、html、图片、音视频等文件可以使用CDN进行加速,客户端发起请求时最近的一个CDN服务器会返回这些内容。这个一般我们程序员不需要太关心,知道有这个事即可,一般的云厂商都有这项服务,开启后即可进行CDN加速。
- Nginx缓存
存放静态资源缓存,当没有CDN时会通过Nginx缓存读取。此外还可以存储IP黑名单、异常用户名单校验等缓存逻辑。