缓存

缓存穿透(cache penetration)

image-20231225212142869

缓存穿透(大概率是遭到恶意攻击):查询一个不存在的数据,mysql查不到数据也就不会直接写入缓存,这就会导致每次请求都查数据库。有可能导致数据库压力过大宕机。

解决方案①:缓存空数据

缓存空数据{key:1,value:null},查询返回的数据为空,仍把这个空结果进行缓存。

优点:简单方便
缺点:如果有大量的getById请求不存在的数据,内存压力就会很大;另外还可能发生缓存和DB数据不一致的问题(redis缓存了空数据,但是后来DB真的更新上这个数据了,redis却还是null)。

解决方案②:布隆过滤器

布隆过滤器的作用就是检索一个元素是否在一个集合当中,从而拦截不存在的数据。布隆过滤器先判断请求的值是否存在:不存在直接拦截返回,存在的话才走redis或DB。前提:缓存预热的时候,会先预热布隆过滤器。

image-20231225213504691

底层实现:先取一个比较大的数组(bitmap aka 位图),里面存放二进制0或1。一开始都是0,每加入一个新key / 尝试查询一个key时,就进行3次hash运算,模于数组长度找到数据的下标,把那个单元改为1。如此就表示该key存在。

它的实现依赖于bitmap。

  • bitmap(位图):相当于是一个以 bit 为单位的数组,数组中每个单元只能存二进制数。

优点:内存占用较少,没有多余的key
缺点:实现相对复杂一些;而且存在误判的可能性。

查到不一定存在,查不到一定不存在。如下图所示。

image-20231225214743019

误判率:数组越小,误判率越大;数组越大,误判率越低,但是同时内存消耗也会越大。
具体实现中,可以用Redisson、Guava等实现方案来设置误判率,误判率设置合适就行,比如5%以内,不至于在高并发的时候压倒数据库。

缓存击穿(cache breakdown)

缓存击穿:给某一个key设置了过期时间,当key过期时,恰好这个时间点对这个key有大量的并发请求进来。这些请求一般都是会从后端DB加载数据并回设到缓存的,但是这时的大并发的请求可能会瞬间把DB压垮。(为什么缓存这么慢呢?因为有的情况下,外面存入缓存的数据可能是多个表汇总的结果,可能需要分组统计。)

image-20231226001324564

解决方案①:互斥锁 (aka 分布式锁) - 强一致,性能差

当缓存失效查询未命中时,不立即去load db,而是先使用比如Redis的setnx去设置一个互斥锁,当操作成功时再进行load db操作并回设缓存,不成功就一直重试get缓存的方法。

image-20231226003428451

解决方案②:逻辑过期 - 高可用,性能优,不能保证数据绝对一致

大概思路是:

  1. 对redis中热点的key不设置过期时间,而是设置一个过期时间字段一块存入缓存。。。,当前key设置过期时间。image-20231226003316282

  2. 当查询的时候,从redis取出数据后判断时间是否过期

  3. 如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,但这个数据并不是最新的。

image-20231226004151215

两种方案各有利弊

如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没那么高,锁需要等,另外也可能产生死锁的问题。(如跟钱相关的业务数据)

如果选择key的逻辑删除,则优先考虑的是高可用性,性能比较高,但是数据同步就做不到强一致了。(如侧重于用户体验的业务)

缓存雪崩(cache avalanche)

缓存雪崩是指在同一时段内大量的缓存key同时失效,或者Redis服务宕机,导致大量请求全部到达DB,DB瞬间访问压力太大,导致“雪崩”。

与缓存击穿的区别:雪崩是很多key;而击穿是某一个key缓存。

情况一:在同一时段内大量的缓存key同时失效(也就是设置缓存时采用了相同的过期时间)

image-20231226004926263

(解决方案①)解决起来比较简单,只需给不同key的TTL添加随机值

情况二:Redis服务宕机

image-20231226005109186

(解决方案②)为了预防这种问题,我们可以通过搭建Redis集群来提高服务的可用性。比如:哨兵模式、集群模式

降级限流策略

(解决方案③)在设计这个业务系统时,可以给缓存业务添加降级限流策略。比较常见的限流设置可以在nginx或spring cloud (微服务) 的gateway网关中去设置限流规则。

  • 可作为系统的保底策略,使用于:穿透、击穿、雪崩。

多级缓存

(解决方案④)可以给业务添加多级缓存,可以预防大量的key过期。可以使用Guava或Caffeine,让它们作为一级缓存,redis作为二级缓存。