一、引入
在提高系统性能的各种方案中,缓存是往往人们最容易想到且有效的一种方式。
缓存的作用便是让数据离使用者更近,访问速度更快。
缓存主要用在两种场景中:
- 读多写少的数据
- 查询时间长、需要经过复杂处理的数据
经常读取的数据、高频访问的数据、热点数据、I/O瓶颈数据、计算昂贵的数据、不变的数据、弱一致性数据
缓存的数据一般由Key与Value两部分组成。
Key设计考虑因素:
- 无碰撞:保证两个不同的数据对应的Key不同,避免命中错误缓存
- 高效生成:给定数据后,使用较小的代价生成对应的Key
- 高效比较:给定两个Key以后,需要快速比较出二者是否一致
二、常见缓存分类
1. 客户端缓存(浏览器)
用户是最初始的请求发起方,与用户交互的模块称为客户端(如浏览器、电脑客户端、手机、嵌入式系统界面等)。
如浏览器缓存,当我们使用浏览器访问一些网站页面或Http访问时,可以根据服务端返回的缓存设置响应头将响应内容缓存到浏览器中,下次可以直接使用缓存内容或者仅需去服务端验证缓存是否过期即可。
2. 网关/反向代理
如首页中的图片、视频、音乐等静态资源可以放在如Nginx等Web服务器上。
在这里可以缓存与用户无关的元素、页面等通用性很强的数据。
3. 本地缓存
指在应用中的缓存组件,应用与缓存在同一个进程内部,没有过多网络开销。
适用于单节点的应用,将数量少、改动小、访问频率高的数据进行缓存。
常用的本地缓存组件有HashMap、Guava Cache、Caffeine。
4. 分布式缓存
常用的分布式缓存包括Redis、Memcached、Tair。其中Redis提供的数据结构丰富且简单易用,所以在应用实践中使用最为广泛。
4. 数据库缓存
如Mysql的Query Cache,SQL Server的Procedure Cache。
三、缓存更新策略
1. Cache-Aside
- 读:先从缓存中读取数据,若存在,直接将数据返回;若数据不存在,则从数据库获取数据并将数据写入缓存
- 写:先将数据写入数据库,成功后让对应的缓存失效
这种模式由业务代码直接维护缓存。
2. Cache-As-SoR
SoR(System of Record):记录系统,即实际存储原始数据系统。
这种模式所有操作都是对Cache操作,然后cache再委托SoR完成数据的读、写操作。即业务代码中只能与Cache交互,而Cache则与数据库交互,进行数据读写。
2.1 Read/Write Through
- 读:先从缓存中读取数据,若存在,直接将数据返回;若数据不存在,由由缓存服务(如Guava Cache中由CacheLoader查询源数据)从数据库读取数据、写入缓存,最后返回数据。
- 写:调用缓存服务进行写操作,缓存服务将数据同时写入缓存和数据库
2.2 Write Behind
这种模式叫回写模式,也称Write Back。
与Write Through同步写入缓存和数据库不同,Write Back在写操作时,先写入缓存,然后异步写入数据库。
异步以后可以实现批量写、合并写、延时和限流。
在日常的实际应用中,Cache-Aside模式较为常见。在这个模式中,缓存用来提高查询速度,可以把它当作一个辅助性的东西;当缓存出现问题时,仍可直接查询数据库完成读写操作。在Cache-As-SoR模式中,调用方只能直接调用缓存服务,真正的原始数据系统对其不可见;此时缓存服务发生问题,则会无法读写数据,相关的功能都会受到影响。
四、缓存清理策略
1. 时效式清理
为缓存的数据设置失效时间,到达时间后自动清理。
时效式清理策略简单易用,限定了数据的存活时间,但是无法限定缓存的空间大小。
2. 数目阈值式清理
数目阈值式清理可以通过限制数据的数目来间接限定缓存空间的大小。
在缓存中的数据达到一定的阈值后,开始根据不同的策略进行清理。
2.1. FIFO
先进先出( First in First out),当缓存中的数据达到上限时,优先清理最先写入的数据
2.2 LRU
最近最少使用(Least Recently Used),如果一条数据最近被访问过,则认为它很可能接下来还会被访问;如果一条数据长期没有被访问,则它很可能接下来依旧不会被访问。
因此,当缓存清理时,优先清理长久未被访问的数据,保留最近被访问过的数据。
2.3 LFU
最不经常使用 (Least Frequently Used),优先淘汰访问频率最小的数据
五、缓存风险
1. 缓存穿透
概念 :调用方查询缓存中不存在的数据,导致缓存无法命中,穿透到后端数据库进行查询。当这样的请求发生高频、大量调用时,对系统造成巨大压力。
**解决方案:
返回空值或业务默认值,并将其进行缓存。
-
布隆过滤器:通过布隆过滤器判断数据是否存在,如果不存在直接返回空值,无需继续查询缓存与数据库。
布隆过滤器可以用于一个元素是否在一个集合中。
a. 使用n个哈希函数对某个数据进行计算,得到n个哈希值
b. 将n个哈希值对bit数据的长度取模,得到每个哈希值在数组中的对应位置
c. 将对应位置的bit设置为1
增加前置的相关校验,避免恶意攻击与爬虫,禁用非法请求
2. 缓存雪崩
概念 :指大量缓存突然失效,给后端服务及数据库造成巨大压力。
解决方案:
- 对不同的数据使用不同的失效时间
- 对同类型数据使用不同的失效时间(固定时长+随机时长)
3. 缓存击穿
概念 :一个高频访问的热点Key突然失效,大量请求同时访问数据库造成巨大压力。
解决方案:
- 加锁(单机、分布式):使同一时刻只能有一个请求访问数据库并更新缓存。
- 设置热点数据永不过期、后台更新:将数据有效期设置为永久,后台定时更新数据,防止数据因内存不足被清理
- 如果这篇文章对你有用的话,点个赞再走呗!
- 如有问题,欢迎留言评论!
- 欢迎转载,烦请注明出处!