1. Redis缓存穿透的概念
Redis作为一款非常流行的缓存数据库,常用于缓存经常查询的数据,以加快服务的响应速度。然而,当一个恶意攻击者故意查询一个不存在于数据库中的数据,这个请求就会瞬间穿透缓存,直接落在了数据库层,从而导致数据库的负荷升高。
这种情况就被称为Redis缓存穿透。攻击者可以通过多次穿透缓存,让数据库过载从而使应用程序崩溃。
Redis缓存穿透的示例
假设这里有一个想要查询用户信息的接口,其查询语句如下:
SELECT * FROM user WHERE userId = 'xxx'
当userID存在于数据库时,查询可以正常返回结果;但当该userID不存在于数据库时,则需要避免将该非法请求直接转发到数据库,因此需要在缓存中进行验证。具体实现方式便是在缓存中检查该userID是否存在,如果存在则直接返回结果,否则返回错误。
// 从redis缓存中查找该userID是否存在
if redisCache.get(userId) != null{
return redisCache.get(userId)
} else {
// 该userID不存在于redis缓存中,则将查询请求转发至数据库
result = database.query('SELECT * FROM user WHERE userId = 'xxx')
// 将查询结果缓存起来,以备下次查询使用
redisCache.set(userId, result)
return result
}
然而,当恶意攻击者发送一个不存在于数据库中的userID(例如以下SQL注入语句),查询返回空结果,进而穿透Redis缓存直接查询数据库,从而占用了DB的大量资源。
userId=1 or 1=1
2. Redis缓存雪崩的概念
Redis缓存雪崩是指缓存中有大量的Key集中在同一时刻失效或集中过期,导致所有的查询请求都落在了数据库层面,从而导致数据库被瞬间压垮。
Redis缓存雪崩的原因
由于Redis中缓存都是有有效期的,而且所有的过期时间都是相同的,因此可能出现所有缓存都在相同的时刻失效,从而导致所有的请求都落在了数据库层面。
另外,缓存集中失效也可能是因为服务器重启、网络故障等原因造成。
Redis缓存雪崩的解决方案
为了避免Redis缓存雪崩,我们可以尝试使用以下方法避免缓存集中失效。
2.1.设置缓存失效时间随机化
对于缓存时间,可以设置为每个Key的过期时间都随机化,虽然过期时间不会有太大的差异,但是可以使那些在同一时间过期的缓存的过期时间分散,避免集中过期。
// 缓存有效时间设置为3小时,并在此基础上加上一个随机的偏移量
final int expireTime = 3 * 60 * 60 + random.nextInt(60 * 60)
redisCache.set(key, value, expireTime)
2.2.存储多份缓存(热备)
建议在分布式系统中,对于同一份缓存,可以将它们同时存储在不同的Redis缓存中,这样即使某一个缓存失效,其他缓存仍然可以正常工作。
2.3.限流和降级
降低对于缓存的依赖,建议给相关操作设置限流和降级策略,当系统压力过大的时候,可以让部分非核心功能降级或者进行限流来缓解系统压力。
另外,对于能够使用锁来避免对同一份缓存的并发访问也是一个不错的选择,以避免雪崩效应。
总结
Redis缓存穿透和缓存雪崩都是由于缓存策略中没有很好地考虑到各种情况而导致的缓存失效问题。为了避免以上问题的发生,我们可以采用多种方法进行缓存优化,例如随机化缓存时间、多份热备缓存、限流降级和并发访问锁等等。