使用redis时经常会遇到经典的三个问题,缓存穿透,缓存击穿和缓存雪崩的问题,那么我们应该如何理解以及如何应对这三个问题哪
缓存击穿
当我们访问的数据先经过缓存,缓存中没有该值或者缓存的值过期,那么该请求会将查询的请求转移到数据库上,这就是缓存击穿的问题.实际这样的情景还是很常见的,
解决方案: 这种情形属于比较常见的,一般的解决方案就是重新查询数据再赋值给对应的key,仅此而已.
缓存穿透
但是如果上百万的请求请求的数据全是控制,那就意味着上百万的请求压力直接打到数据库层级上.这对数据库压力来说是致命的.这种情形就是缓存穿透.
解决方案: 通常就是将查询的结果在一段时间内赋空值,即setex(key,val,time),这种情形可以有效的将一些请求过滤掉,时间到期后再查询一边数据库,如果有的话就赋值给对应的key,如果没有就继续给key赋空值.
缓存雪崩
缓存雪崩是比较难以处理的问题,解决雪崩的方案要考虑限流以及分布式锁的一些问题
缓存雪崩就是在同一时刻,redis服务器上的缓存失效,发生大量的缓存击穿,请求全部打在数据库层级上,这无疑对数据库造成很大的压力,糟糕的情况可能会造成数据库宕机.
因此我们要合理调整这些请求的请求顺序,经典的解决方案就是使用分布式锁进行限制访问数据库的请求,这种的缺点就是一次只有一个请求被通过去访问数据库,当然我们仅对同一个数据进行加锁顺序访问.
即假设有1000个请求需要查询商品1的信息,我们对商品1进行加锁限制访问,每一次仅允许通过一个请求进行查询,如果查询出来之后将数据存储到缓存上,那么下一次其它的数据通过***自旋***重新查询就会查询到缓存上的数据了.
解决方案脑图
其中会遇到两个经典的问题
- 如果在redis中的锁已经过期了,然后锁过期的哪个请求又执行完毕回来删除,删除了其它线程的锁怎么办.
解决方案: 在设置的分布式锁中的value使用,随机字符串进行设置,删除锁之前判断下当前线程的分布式锁的value是否是自己线程的value,如果是的话就删除,如果不是的话就不进行删除锁 - 如果碰巧在查询redis锁还没删除的时候,正在进行网络传输吗,锁过期了怎么办 , 即判断语句执行的时候, 锁没有过期,判断结束了,锁过期了
这种情形的根本原因就是操作非原子性的原因,因此需要借助lua脚本进行原子设置
确保了原子操作