概述:
Redis中大量缓存数据在同一时间过期(失效)或者Redis故障宕机时,如果此时有大量的用户请求,都无法在Redis中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃。
解决方法如下:
1.设置过期时间
大量的数据设置不同的过期时间,避免同时过期。也可以选择给过期时间加上一个随机数,这样就可以不会在同一时间过期。
2.互斥锁
发现访问的数据不在Redis中,加上互斥锁,保证同一时间内只有一个请求来构建缓存( 从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
eg:
实现互斥锁的时候,最好设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这时 其他请求也一直拿不到锁,整个系统就会出现无响应的现象。
扩展:
缓存击穿:
如果缓存中某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易被高并发的请求冲垮。缓存击穿跟缓存雪崩很相似,也可以认为缓存击穿是缓存雪崩的一个子集。
解决方法:
1.互斥锁方案,保证同一时间只有一个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
2.不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间;
缓存击穿和缓存雪崩的区别:
缓存雪崩:在某一个时间段,缓存数据同时失效。导致大量请求直接到达数据库,导致数据库瞬间压力骤增,设置宕机。
缓存击穿:针对某一个key(热点数据)的请求,大量并发请求都查询这一个数据,但是该数据不在缓存中,导致多请求直接访问数据库,而其他请求被阻塞等待该请求的返回结果,导致系统性能下降。也就是说,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
缓存穿透:
当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据。那么此时大量的请求都落在数据库中,导致数据库压力骤增,这就是缓存穿透问题。
导致原因:
1. 业务误操作,缓存中的数据和数据库中的数据都被误删除了,所以导致缓存和数据库中都没有数据;
2. 恶意攻击,故意大量访问某些读取不存在数据的业务;
解决方法:
1.限制非法请求,进行参数校验,对于不合法的参数请求直接抛出异常返回给客户端。
2.缓存空值或者默认值,当发现缓存穿透的现象时,可以针对查询的数据,在缓存中设置一个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值,返回给应用,使其不会继续查询数据库。
3。使用布隆过滤器,在写入数据库数据时,使用布隆过滤器做个标记,然后用户发起请求后,判断请求值是否存 在于布隆过滤器,如果不存在 那这个请求直接无效,如果存在 那就在缓存中查找对应的数据。这样当出现大量请求的时候,会先查询布隆过滤器和Redis,不会对数据库造成压力。
布隆过滤器说数据不存在的话,那么数据库中一定不会有这个数据。如果说数据存在,并不一定证明数据库中存在这个数据,有误判的几率,只不过几率非常小。
布隆过滤器的原理:
布隆过滤器由「初始值都为 0 的位图数组」和「 N 个哈希函数」两部分组成。当我们在写入数据库数据时,在布隆过滤器里做个标记,这样下次查询数据是否在数据库时,只需要查询布隆过滤器,如果查询到数据没有被标记,说明不在数据库中。
三个操作完成标记:
1.使用 N 个哈希函数分别对数据做哈希计算,得到 N 个哈希值;
2.将第一步得到的 N 个哈希值对位图数组的长度取模,得到每个哈希值在位图数组的对应位置;
3.将每个哈希值在位图数组的对应位置的值设置为 1;
例如:
在数据库写入数据 x 后,把数据 x 标记在布隆过滤器时,数据 x 会被 3 个(假设布隆过滤器的哈希函数为3 个)哈希函数分别计算出 3 个哈希值,然后在对这 3 个哈希值对 数据x的长度 取模,假设取模的结果为 1、3、5,然后把位图数组的第 1、3、5 位置的值设置为 1。当应用要查询数据 x 是否存在于数据库时,通过布隆过滤器只要查到位图数组的第 1、3、5 位置的值是否全为 1,只要有一个为 0,就认为数据 x 不在数据库中。
布隆过滤器由于是基于哈希函数实现查找的,高效查找的同时存在哈希冲突的可能性,比如数据 x 和数据 y 可能都落在第 1、3、5的位置,事实上,可能数据库中并不存在数据 y,存在误判的情况。