缓存(redis)四大问题

使用缓存的思路:先查询缓存,若不存在则查询数据库,之后把数据放在缓存一份。

一、缓存穿透

使用不存在的key进行大量高并发查询,导致缓存无法命中,每次请求都穿透到数据库查询,数据库压力过大(缓存中没有数据,数据库中也没数据)

例如:请求id为1的数据,id为1的数据在数据库中根本就不存在,总是会请求数据库。好似缓存不存在一样(没有起到保护数据库作用)。

解决方案:

  1. 接口增加校验拦截非法请求,比如拦截id<0的数据。
  2. 将空值缓存起来
  3. 使用布隆过滤器(redis4.0之后有布隆过滤器插件可用,还可以基于redis位图实现布隆过滤器)

二、缓存击穿

缓存中没有数据,但数据库中有数据(一般是缓存时间到期),但是由于并发用户特别多,同时读缓存没读到,而去查询数据库,引起数据库瞬间压力过大。

解决方案:

  1. 使用互斥锁,一般jvm锁。如果是多部署则使用分布式锁
  2. 热点数据永不过期

三、缓存雪崩

缓存服务器重启,或是大量key在集中在某个时间段失效,引起导致数据库压力巨大甚至宕机。

解决办法:

  1. 对不同的数据使用不同的失效时间,相同的数据过期时间也增加一个随机数(错开失效时间如:60+(int)(Math.random()*3600))
  2. 热点数据永不过期
  3. 搭建高可用集群,保证高可用。

四、缓存缓存与数据库一致性

数据修改的三种操作:

(1)先更新数据库,再更新缓存(最差劲方式)

如果修改数据库成功,修改缓存失败则很容易出现数据不一致问题

(2)先更新缓存,再更新数据库

也有问题:

当多线程时,由于线程A比线程B先更新数据库,最终数据库的结果是线程B的值

但是由于网络原因线程B比线程A先更新缓存,最终缓存的结果是线程A的值

数据库是B,缓存是A,出现数据不一致问题

(3)先删除缓存,再更新数据库(推荐)

也有问题:

当多线程时,线程A删除了缓存,想要更新数据库时,线程B发现缓存为空,去查询数据库,并把结果加入到缓存中(旧值)

而线程A更新了数据库(新值)。好似缓存根本就没有被删除掉一样。缓存和数据库还是不一致

解决办法:

1、延时双删

线程A在更新数据库结束之后,再删除一遍缓存,但是要延时。目前大多数企业使用此方法,好维护效率也不低。

不过这种方法能解决大概率问题,并不能做到100%。

2、串行化

解决的主要是上面的线程A更新数据库、线程B查询数据库并加入缓存这两步的顺序,顺序确定了“先删除缓存,再更新数据库”并发问题就解决了。可以使用队列来控制

当线程A删除了缓存,想要更新数据库时,把更新数据库这步操作加入队列。线程B发现缓存为空,去查询数据库,把查询数据库这步操作加入队列。

这就保证了从队列中取出是先更新数据库,再查询数据库。

使用队列方式:

  • 如果是单机,则可以使用java队列
  • 如果是分布式,则可以使用mq或redis队列

猜你喜欢

转载自blog.csdn.net/sumengnan/article/details/113098331