1.1、缓存穿透
就是请求一个数据,先查询缓存,没有,然后请求数据库,数据库里也没有这个数据,这样的多次请求会很大的消耗数据库的性能或者耗尽数据库资源达到的一个攻击手段。
如何防止
第一种就是对发送的请求数据进行一个判断是不是符合数据库中的查询字段,不符合直接返回不让他去查询数据库。
第二种是当查询到数据库中没有之后,在缓存中存入一个空值或者任意的特殊值,来避免每次查询时都去查询数据库。
第三种就是使用布隆过滤器。
使用多个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点,将Bit array理解为一个二进制数组,数组元素是0或1。
当一个元素加入集合时,通过N个散列函数将这个元素映射到一个Bit array中的N个点,把它们设置为1。检索某个元素时再通过这N个散列函数对这个元素进行映射,根据映射找到具体位置的元素,如果这些位置有任何一个0,则该元素一定不存在,如果都是1也不一定存在,因为可能出现hash碰撞。
1.2、缓存击穿
缓存击穿就是在大量的用户同时访问同一个热点数据,而这个热点数据又在某一时间失效,这样就会有大量的请求去访问数据库瞬间耗尽数据库资源,导致数据库无法使用。
如何防止
第一种就是加锁,单体架构使用同步锁控制访问数据库的代码,只让一个线程去访问,查到的数据放入缓存。分布式架构中使用分布式锁。
第二种让这些热点数据不过期,这样就再多的请求过来都是去访问缓存,而不是去直接访问数据库。
第三种就是对类似的热点数据进行缓存预热提前写入缓存,使用定时任务在特定的时间去更新。
第四种就是降级逻辑,查询热点数据的接口中,让他在缓存中查不到数据时就走降级逻辑不让他去查询数据库。
1.3、缓存雪崩
缓存雪崩就是大量的key几乎同时失效,在高并发的情况下导致大量请求访问数据库造成的雪崩问题。
如何防止
第一种加锁,跟缓存击穿一样。
第二种通常是对同一类型的key设置随机的过期时间,不让他同一时间内大量的key失效。
第三种缓存预热。
2、如何保存缓存一致性
这个讲的是缓存和数据库信息的一致性,数据库更改了之后,缓存内还是老数据,可能会造成短暂的数据不一致
解决方法
使用分布式锁,对数据库进行的操作上锁。
延时双删,这里主要有一个主从数据库的问题,让一个线程直接删除缓存,这个线程写完数据库之后,设置一个延迟时间再次去删除缓存,这样即使别的线程获取了旧数据并且写到了缓存里,旧数据也会被删掉,然后被写入新的数据。
通过Canal加MQ异步任务方式,这个就是在线程对数据库写数据时,Canal会通过一个binlog日志去监听操作,然后将数据变化的消息写进mq里,异步的程序监听这个mq进行实时的缓存更新来解决缓存与数据库的不一致。