多级缓存方案
目标
承载亿级流量
核心思路
多级缓存就是充分利用请求处理的每个环节,分别添加缓存,减轻Tomcat压力,提升服务性能:
缓存分类
缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。我们把缓存分为两类:
分布式缓存,例如Redis:
优点:存储容量更大、可靠性更好、可以在集群间共享
缺点:访问缓存有网络开销
场景:缓存数据量较大、可靠性要求较高、需要在集群间共享
Caffeine
Caffeine是一个基于Java8开发的,提供了近乎最佳命中率的高性能的本地缓存库。目前Spring内部的缓存使用的就是Caffeine。GitHub地址:https://github.com/ben-manes/caffeine
进程本地缓存,例如HashMap、GuavaCache:
优点:读取本地内存,没有网络开销,速度更快
缺点:存储容量有限、可靠性较低、无法共享
场景:性能要求较高,缓存数据量较小
缓存链路
无缓存链路
- 客户端请求 Nginx
- Nginx 转发请求到 Tomcat
- Tomcat 查询数据库响应请求给Nginx
- Nginx 响应请求给客户端
分析:
- 客户端是流量来源,不会称为系统的性能瓶颈
- Nginx 是系统流量入口,仅负责转发,相对与接收请求并执行业务逻辑的Tomcat,短板会在于 Tomcat
- Tomcat 是业务的执行者,业务数据来源于数据库,Tomcat 中简单的一条语句就可以让数据库执行一个复杂查询,相对数据库而言,短板会在于数据库
- 整体上,根据最容易出现性能瓶颈的概率排序,Nginx < Tomcat < 数据库
进程缓存链路
- 客户端请求 Nginx
- Nginx 转发请求到 Tomcat
- Tomcat 线程访问进程中的缓存数据,命中则获取到数据
- 未命中则查询数据库
- Tomcat 响应请求给Nginx
- Nginx 响应请求给客户端
分析:
在【无缓存链路】的基础上,使用进程缓存减少数据库压力,理想情况下,缓存100%命中,此时性能瓶颈落在Tomcat上,另外分布式环境下,进程缓存会导致数据不一致,这使得进程缓存的应用受到限制
经典分布式缓存链路
- 客户端请求 Nginx
- Nginx 转发请求到 Tomcat
- Tomcat 首先查询 Redis 看是否命中缓存,命中则获取到数据
- 未命中则查询数据库获取到数据
- Tomcat 响应请求给 Nginx
- Nginx 响应请求给客户端
分析:
在【无缓存链路】方案的基础上,在数据库这里做了优化,优先从 Redis 中读取缓存数据,期望解决数据库性能瓶颈。假定缓存必然被命中,则此时性能瓶颈会转移到 Tomcat。即Tomcat处理该请求的耗时比直接请求缓存数据耗时还长得多。此时解决了分布式环境下若干台Tomcat缓存不一致问题,但Tomcat成为新的性能瓶颈这个问题并未解决。
缓存代理链路
在【经典分布式缓存链路】基础上,将缓存前移,使用专门的代理用于代理请求缓存,而非缓存请求则转发给Tomcat,由于 OpenResty 比 Tomcat 性能高,此时在链路上提高了性能
完全多级缓存链路
OpenResty 支持本地缓存,客户端也可以有一份缓存,Tomcat 进程缓存可以选择性使用
- 客户端缓存
- 缓存代理本地缓存
- 分布式缓存
- Web服务进程缓存
流程
- 客户端首先查看本地缓存,命中缓存则直接返回数据
- 未命中缓存,客户端请求 Nginx
- Nginx 转发请求到 OpenResty
- OpenResty 首先看是否命中本地缓存,若命中本地缓存则直接返回数据
- 未命中缓存,OpenResty 查询 Redis 看是否命中缓存,命中则获取到数据直接返回
- 未命中缓存则向 Tomcat 发起请求
- Tomcat 查询数据库获取到数据
- Tomcat 响应请求给 OpenResty
- OpenResty响应请求给Nginx
- Nginx 响应请求给客户端
冷启动与缓存预热
冷启动:服务刚刚启动时,Redis 中并没有缓存,如果所有商品数据都在第一次查询时添加缓存,可能会给数据库带来较大压力。
缓存预热:在实际开发中,我们可以利用大数据统计用户访问的热点数据,在项目启动时将这些热点数据提前查询并保存到Redis中。
缓存重建
缓存同步
缓存同步方案
缓存数据同步的常见方式有三种:
设置有效期:给缓存设置有效期,到期后自动删除。再次查询时更新
优势:简单、方便
缺点:时效性差,缓存过期之前可能不一致
场景:更新频率较低,时效性要求低的业务
同步双写:在修改数据库的同时,直接修改缓存
优势:时效性强,缓存与数据库强一致
缺点:有代码侵入,耦合度高;
场景:对一致性、时效性要求较高的缓存数据
异步通知:修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据
优势:低耦合,可以同时通知多个缓存服务
缺点:时效性一般,可能存在中间不一致状态
场景:时效性要求一般,有多个服务需要同步
异步通知方案:
基于 MQ 的异步通知方案
基于 Canal 的异步通知方案
最终缓存方案