文章目录
一、缓存预热
缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
二、缓存雪崩
指的是在短时间内,大量的键过期导致请求直接打到数据库,或者是Redis宕机,导致请求直接打到数据库,而数据库无法处理如此大量的请求,导致数据库宕机,进而造成整个系统或者服务的不可用。牵一发而动全身。
解决方案:
- Redis缓存集群实现高可用(合理设置过期时间或者延时,采用哨兵集群等)
- 采用多级缓存策略,例如 Nginx缓存+Redis缓存+ehcache缓存
- 采用Hystrix或者阿里sentinel进行服务限流限流或者降级
- 监控Redis各项指标,即使灾难预警
- 采用AOF/ RDB持久化,尽快恢复Redis集群
三、缓存击穿
缓存击穿就是单个热点key突然失效或者过期,导致大量的请求未命中Redis之后打在数据库上,导致数据库压力剧增
解决方案
- 对于热点key加长过期时间,或者干脆不设置过期时间
- 二级缓存设置不同的失效时间,保证不会同时失效
- 如果缓存中没有该key则加锁,保证只会有一个线程打到数据库进行查询(效率较低)
四、缓存穿透
缓存穿透指的是⼤量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本
没有经过缓存这⼀层,并且在数据库中也没有该跳数据,导致也没有继续回写到缓存中,导致大量请求直接打到数据库层面
解决方案:
- 缓存空对象或者缺省值
- 如果在数据库也无法查询,同样回写到缓存中null值,第二次请求就不会落到数据库中
- 如果黑客恶意攻击每次采取不同的ID那么缓存将越写越多,所以要设置过期时间,并且大量请求还是直接打中了数据库所以该方案有缺陷
- 使用布隆过滤器,下一章详细讲解
五、布隆过滤器
想要尽量避免缓存穿透,一个办法就是对数据进行预校验,在对Redis和数据库进行操作前,先检查数据是否存在,如果不存在就直接返回。如果我们想要查询一个元素是否存在,要保证查询效率,可以选择HashSet,但是如果有10亿个数据,都用HashSet进行存储,内存肯定是无法容纳的。这时就需要布隆过滤器了
布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(bit数组)和一系列随机映射函数(hash)。布隆过滤器可以用于检索一个元素是否在一个集合中
特点:
- 高效地插入和查询,占用空间少,返回的结果是不确定性的。
- 一个元素如果判断结果为存在的时候元素不一定存在,但是判断结果为不存在的时候则一定不存在。
- 布隆过滤器可以添加元素,但是不能删除元素。因为删掉元素会导致误判率增加。
- 误判只会发生在过滤器没有添加过的元素,对于添加过的元素不会发生误判。
1. 原理实现
① 初始化
布隆过滤器 本质上 是由长度为 m 的位向量或位列表(仅包含 0 或 1 位值的列表)组成,最初所有的值均设置为 0
② 添加
当我们向布隆过滤器中添加数据时,为了尽量地址不冲突,会使用多个 hash 函数对 key 进行运算,算得一个下标索引值,然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。
例如,我们添加一个字符串wmyskxz
③ 查询
向布隆过滤器查询某个key是否存在时,先把这个 key 通过相同的多个 hash 函数进行运算,查看对应的位置是否都为 1,
只要有一个位为 0,那么说明布隆过滤器中这个 key 不存在;
如果这几个位置全都是 1,那么说明极有可能存在;
为什么说极有可能呢?
因为这些位置的 1 可能是因为其他的 key 存在导致的,也就是hash冲突
就比如我们在 add 了字符串wmyskxz数据之后,很明显1/3/5 这几个位置的 1 ,如上图,是因为第一次添加的 wmyskxz 而导致的;此时我们查询一个没添加过的不存在的字符串inexistent-key,它有可能计算后坑位也是1/3/5 ,这时候经过hash函数计算以及查询发现1 /3 / 5 的位置都是1,所以布隆过滤器认为该数据是存在的,就发生了误判
为什么key不能删除呢?
因为该key经过运算放入bit数组中的值,并不一定是你产生的,有可能其他数值也共用着这个数位,如果你删除了该位置,别的key进行查询时可能导致查询失败
2. Google工具包Guava实现布隆过滤器
- 首先将maven中导入相关依赖(其他一些SpringBoot整合redis依赖就不赘述了)
<!--Google 开源的 Guava 中自带的布隆过滤器-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
- 模拟秒杀场景获取商品详情页面的单机版Guava实现布隆过滤器
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.Data;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.