Redis缓存及热点key问题解决方案

网友投稿 513 2023-06-14

Redis缓存及热点key问题解决方案

Redis缓存及热点key问题解决方案

今天又学到了很多,感觉雪崩和穿透很有意思理解起来也比较清晰,然后我搜索了一些资料,给自己做一个普及

我们通常使用 缓存 + 过期时间的策略来帮助我们加速接口的访问速度,减少了后端负载,同时保证功能的更新

缓存穿透

缓存系统,按照KEY去查询VALUE,当KEY对应的VALUE一定不存在的时候并对KEY并发请求量很大的时候,就会对后端造成很大的压力。

(查询一个必然不存在的数据。比如文章表,查询一个不存在的id,每次都会访问DB,如果有人恶意破坏,很可能直接对DB造成影响。)

由于缓存不命中,每次都要查询持久层。从而失去缓存的意义。

解决方法

1、缓存层缓存空值。

–缓存太多空值,占用更多空间。(优化:给个空值过期时间)

–存储层更新代码了,缓存层还是空值。(优化:后台设置时主动删除空值,并缓存把值进去)

2、将数据库中所有的查询条件,放到布隆过滤器中。当一个查询请求来临的时候,先经过布隆过滤器进行检查,如果请求存在这个条件中,那么继续执行,如果不在,直接丢弃。

备注:

比如数据库中有10000个条件,那么布隆过滤器的容量size设置的要稍微比10000大一些,比如12000.

对于误判率的设置,根据实际项目,以及硬件设施来具体决定。但是一定

不能设置为0,并且误判率设置的越小,哈希函数跟数组长度都会更多跟更长,那么对硬件,内存中间的要求就会相应的高。

private static BloomFilter bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.

0001);

有了size跟误判率,那么布隆过滤器就会产生相应的哈希函数跟数组。

综上:我们可以利用布隆过滤器,将redis缓存击穿控制在一个可容忍的范围内。

缓存雪崩(缓存失效)

如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。

缓存层宕掉后,流量会像奔逃的野牛一样,打向后端存储

解决方法:

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存

不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀

做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

热点key

(1) 这个key是一个热点key(例如一个重要的新闻,一个热门的八卦新闻等等),所以这种key访问量可能非常大。

(2) 缓存的构建是需要一定时间的。(可能是一个复杂计算,例如复杂的sql、多次IO、多个依赖(各种接口)等等)

于是就会出现一个致命问题:在缓存失效的瞬间,有大量线程来构建缓存(见下图),造成后端负载加大,甚至可能会让系统崩溃 。

解决方法:

1. 使用互斥锁(mutex key):这种解决方案思路比较简单,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据就可以了

2. "提前"使用互斥锁(mutex key):在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。

3. "永远不过期":

这里的“永远不过期”包含两层意思:

(1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

(2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

4. 资源保护:可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。

四种方案对比:

作为一个并发量较大的互联网应用,我们的目标有3个:

1. 加快用户访问速度,提高用户体验。

2. 降低后端负载,保证系统平稳。

3. 保证数据“尽可能”及时更新(要不要完全一致,取决于业务,而不是技术。)

所以第二节中提到的四种方法,可以做如下比较,还是那就话:没有最好,只有最合适。

解决方案

优点

缺点

简单分布式锁(Tim yang)

 1. 思路简单

2. 保证一致性

1. 代码复杂度增大

2. 存在死锁的风险

3. 存在线程池阻塞的风险

加另外一个过期时间(Tim yang)

 1. 保证一致性

同上 

不过期(本文)

1. 异步构建缓存,不会阻塞线程池

1http://. 不保证一致性。

2. 代码复杂度增大(每个value都要维护一个timekey)。

3. 占用一定的内存空间(每个value都要维护一个timekey)。

资源隔离组件hystrix(本文)

1. hystrix技术成熟,有效保证后端。

2. hystrix监控强大。

1. 部分访问存在降级策略。

总结

1. 热点key + 过期时间 + 复杂的构建缓存过程 => mutex key问题

2. 构建缓存一个线程做就可以了。

3. 四种解决方案:没有最佳只有最合适。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Boot监听Redis Key失效事件实现定时任务的示例
下一篇:IDEA安装lombok插件设置Enable Annotation Processing后编译依然报错解决方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~