首先要知道使用缓存的逻辑:

1、先尝试从缓存中读取数据。

2、若缓存中没有数据或者数据过期,再从数据库中读取数据保存到缓存中。

3、最终把缓存数据返回给调用方。

这种逻辑唯一麻烦的地方是,当用户发来大量的并发请求时,它们会发现缓存中没有数据,那么所有请求会同时挤在第2步,此时如果这些请求全部从数据库读取数据,就会让数据库崩溃。

数据库的崩溃可以分为3种情况。

1、单一数据过期或者不存在,这种情况称为缓存击穿

缓存失效后三种解决方案_缓存

解决方案:第一个线程如果发现Key不存在,就先给Key加锁,再从数据库读取数据保存到缓存中,最后释放锁。如果其他线程正在读取同一个Key值,那么必须等到锁释放后才行。

2、数据大面积过期或者Redis宕机,这种情况称为缓存雪崩

缓存失效后三种解决方案_缓存_02

解决方案:设置缓存的过期时间为随机分布或设置永不过期即可。

3、一个恶意请求获取的Key不在数据库中,这种情况称为缓存穿透

缓存失效后三种解决方案_缓存_03

比如正常的商品ID是从100000到1000000(10万到100万之间的数值),那么恶意请求就可能会故意请求2000000以上的数据。这种情况如果不做处理,恶意请求每次进来时,肯定会发现缓存中没有值,那么每次都会查询数据库,虽然最终也没在数据库中找到商品,但是无疑给数据库增加了负担。

解决方案有两种:①在业务逻辑中直接校验,在数据库不被访问的前提下过滤掉不存在的Key。②针对恶意请求的Key存放一个空值在缓存中,防止恶意请求骚扰数据库。

上面这些逻辑都是在确保查询数据的请求已经过来后如何适当地处理,如果缓存数据找不到,再去数据库查询,最终是要占用服务器额外资源的。那么最理想的就是在用户请求过来之前把数据都缓存到Redis中。这就是缓存预热

其具体做法就是在深夜无人访问或访问量小的时候,将预热的数据保存到缓存中,这样流量大的时候,用户查询就无须再从数据库读取数据了,将大大减小数据读取压力。