Redis——缓存雪崩、击穿、穿透
缓存雪崩
大量缓存数据在同一时间过期或者Redis故障宕机时,若此时有大量请求,都会直接访问到数据库,导致数据库压力倍增甚至宕机。
大量数据同时过期解决方案:
1、均匀设置过期时间:
设置过期时间的时候可以追加一个随机数,避免数据同一时间过期。
2、互斥锁:
业务线程处理用户请求时,如果发现访问的数据不在Redis里,则加入互斥锁,保证同一时间只有一个业务线程访问数据库并构建缓存。未获取到互斥锁的请求要么等待锁释放后获取缓存,要么返回指定值。注意:互斥锁应该设置过期时间,避免获取锁的线程意外阻塞导致锁无法释放,造成无响应的情况。
3、后台更新缓存:
将更新缓存的工作交给后台线程进行更新
- 第一种方式:后台频繁检测缓存是否有效,检测到缓存失效后(可能是内存资源不足被淘汰的)就马上访问数据库并更新到缓存。
- 缺点:
- 检测时间间隔不能太长,一般是毫秒级,有延迟问题
- 频繁检测存在性能开销
- 第二种方式:业务线程发现缓存失效后,通过消息队列发送一条信息通知后台线程更新缓存。后台线程收到消息后进行判断数据是否已被缓存,没有则访问数据库构建缓存。
- 优点:
- 消息队列中可根据相同请求幂等性实现互斥锁的效果,无需加锁,所有请求都等待缓存构建或返回指定值即可。
- 消息队列具有削峰作用,高并发时也能保证数据库正常运行
业务刚上线时我们就可以提前把数据缓存起来,而不是等待用户来触发缓存构建,这就是所谓的缓存预热。
Redis宕机解决方案:
1、服务熔断或请求限流机制
- 服务熔断机制,暂停业务对缓存服务的访问,直接返回错误,而不是继续访问数据库,直到Redis恢复正常。
- 请求限流机制,只接收少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,直到待Redis恢复正常
2、构建Redis高可靠集群
当前Redis宕机后依然可通过其他从节点获取缓存。
缓存击穿
缓存中的某个热点数据过期,此时大量请求直接访问到数据库。(缓存雪崩是多种多个数据请求,缓存击穿是访问热点数据的大量请求)
解决方案:
- 互斥锁:保证同一时间只有应该业务线程共享缓存。
- 后台异步更新缓存:不再给热点请求设置过期时间,或热点数据快过期时通知后台线程更新缓存并重新设置缓存时间。
缓存穿透
发生缓冲雪崩或击穿时,数据库中是有对应数据的,而缓冲穿透则是数据库中也无法获取到对应数据的情况。如果数据库无法获取数据就无法构建缓存,造成缓存失效。通常发生于业务误操作删除了数据和恶意访问不存在的数据。
解决方案:
1、限制非法请求:
在访问缓存或数据库前判断请求的参数是否合理,过滤不合理请求
2、缓存空值或默认值
若业务发现有缓存穿透的现象,可以针对查询数据在缓存中设置空值或者默认值
3、布隆过滤器
使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断
布隆过滤器的实现:
由初始值都为0的位图数组(Bitmap,连续二进制位序列)和N个哈希函数两部分组成。我们在写入数据库数据的同时对布隆过滤器做标记,这样后续的查询就可根据布隆过滤器快速判断数据是否存在。
过滤操作:
- 第一步,使用N个哈希函数分别对数据进行哈希计算,得到N个哈希值
- 第二步,将第一步得到的N个哈希值对位图数组的长度进行取模,得到每个哈希值的对应位置
- 第三步,将每个哈希值在位图数组中的对应位置设为1
假设有一个长度为8的位图数组,3个哈希函数的布隆过滤器:
查询时,分别计算出数据N个哈希值对应的位置并判断是否全为1。只要有一个为0就说明数据不存在。
布隆过滤器也是存在哈希冲突的,哈希冲突时可能将不存在的查询数据误判为已存在(与数据库中存在的数据发生了哈希冲突)。但数据库中不存在的数据在布隆过滤器中就一定不存在
通过增加哈希函数的数量,可尽量减少因为哈希冲突发生误判的情况。