前言
几种现象很常见,基本都是在高并发的场景下,由于大量请求没有在缓存中查询到数据而从数据库中查询,造成了数据库的并发量瞬间增大。
比如2000的QPS,对于Redis来说顶的很轻松,但是普通服务器上的数据库就很难顶。
下面对这集中情况进行一个简单介绍,并总结一些解决方案。
一、缓存雪崩
1、现象
项目中,通常会将热点数据保存到Redis并设置过期时间,比如在12点将热点数据放到Redis中,过期时间为1小时。
请求时,先去Redis读取数据,如果Redis中不存在该数据,就去数据库中读取数据,然后放到缓存中。
这种设计在并发大时会出现一个问题。
如上面的设计,在1点、2点、3点……的时候,如果缓存失效时,系统并发请求过大,就会对数据库造成周期性的压力。
其实缓存的集中过期并不是非常致命的,最致命的雪崩情况是缓存服务器失效(死机、断网)。
之所以这么说,是因为缓存集中过期造成的缓存雪崩,也会集中创建缓存,数据库兴许可以顶住。
但因为缓存服务节点失效而造成的缓存雪崩,对数据库造成的压力则是难以预估的,很有可能把数据库压垮,造成系统不可用。
这种情况下,如果缓存因为过期时间到达而失效,那么就会有大量的请求打到数据库,造成周期性的数据库压力。
总结:缓存突然不可能,接着来的大量请求瞬间打在了数据库。
2、解决方案
1、随机设置缓存的过期时间:在指定的缓存过期时间上,随机在加上一定时间的过期,不让缓存在同一时间过期,比如超时时间是固定的5分钟,可以随机加上2分钟内的秒数。
2、缓存备份,在A缓存节点设置过期时间,B缓存节点不设置过期时间,当A失效时去读取B。
3、不设置过期时间,在修改数据库时更新缓存。
二、缓存穿透
1、现象
缓存穿透是指,在缓存和数据库中都没有的数据,而用户不断发起请求,造成数据库压力增大。
例如,数据库中的数据主键使用自增,从1开始。但是用户不断查询主键为“-1”的数据,或主键特别大的不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
2、解决方案
1、在应用程序层增加校验,如用户鉴权校验,主键基础校验等。
2、应用程序做限流、熔断、降级,可以从网关层进行限流(例如在Nginx中进行配置),或者使用类似Hystrix的组件进行熔断或降级。
3、使用布隆过滤器,先判断要查询的主键是否存在。
4、使用互斥锁。
三、缓存击穿
1、现象
缓存击穿是指缓存中没有,但数据库中有的数据。
例如,缓存中保存了一个非常热的数据,当缓存过期的同时,并发读取这个热数据的用户特别多,同一时间读缓存但没读到数据,又同时去数据库去取数据,造成了数据库的压力瞬间增大,这种情况就叫缓存击穿。
2、解决方案
1、设置热点数据永远不过期,更新数据库的同时更新缓存。
2、使用互斥锁;