在使用redis中可能会发生以下情况:
缓存出现的问题
缓存穿透
访问redis一个(或多个)不存在的数据,击穿redis到达DB。查询DB仍返回空。大量这样的访问可能会使DB崩溃。
缓存雪崩
缓存系统同一时间有大量数据失效。大量数据打到DB使DB压力过大。
缓存并发
和上面类似,不过这个是当一个key过期时大量请求打到DB。
解决方法
以上三种情况区别在于缓存穿透是恶意的,即通过大量构造不存在的key攻击系统。而后两个缓存雪崩和缓存并发则是缓存失效时出现的问题。缓存雪崩是大量key失效,缓存并发是某个(或某些)缓存失效。
先看缓存穿透,是恶意构造大量不存在的key攻击系统。解决方法:
- 在redis层之前做一层过滤,判断查询的key是否符合规则(或者布隆过滤,或者依据key的生成规则进行过滤)。若不符合直接返回,可拦截恶意请求。
- 若判断不出key的真假,查询到达DB后没有数据,则将空值也写入redis。下次查询就直接打到缓存了(缓存设置较短的过期时间)。
下面是缓存雪崩和缓存并发。
缓存雪崩在于同时大量缓存失效,可在设置缓存时为数据设置不同的过期时间,可大大避免缓存同时失效。
缓存并发在于某些缓存失效了,这个无法避免,缓存只要有过期时间就有失效的那一刻。当大量请求过来时,这里可以使用分布式锁限制对于同一个key的查询,某个时刻只有一个线程可以查询DB。具体:
请求到达redis击穿后(redis没有查询到),根据请求key访问分布式锁,获取锁的线程才可以访问DB并更新缓存。这样就可以限制每个访问同一个key的多个线程只有一个才能访问DB并更新缓存。(分布式锁设置过期时间,避免被锁死,没有获取锁的线程可以休眠一段时间后重试获取缓存)
总结过来就是:
- 在redis层之前做一层过滤,判断查询的key是否符合规则(或者布隆过滤,或者依据key的生成规则进行过滤)。若不符合直接返回,可拦截恶意请求。
- 请求在redis中没有到,则根据请求key访问分布式锁,获取锁的线程才可以访问DB并更新缓存(更新缓存时若在DB中没有查到,也更新缓存,只是更新一个特殊空值,设置过期时间)。这样就可以限制每个访问同一个key的多个线程只有一个才能访问DB并更新缓存。(分布式锁设置过期时间,避免被锁死,没有获取锁的线程可以休眠一段时间后重试获取缓存)
- 将可能成为热点的key的过期时间设置不一样,防止缓存雪崩。