缓存穿透、缓存并发、缓存失效之思路变迁缓存穿透、缓存并发、缓存失效之思路变迁

我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题:

  • 缓存穿透
  • 缓存并发
  • 缓存失效

一、缓存穿透

 

 

注:
上面三个图会有什么问题呢?

我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了。

那这种问题有什么好办法解决呢?

要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
有一个比较巧妙的作法是,可以将这个不存在的key预先设定一个值。
比如,”key” , “&&”。
在返回这个&&值的时候,我们的应用就可以认为这是不存在的key,那我们的应用就可以决定是否继续等待继续访问,还是放弃掉这次操作。如果继续等待访问,过一个时间轮询点后,再次请求这个key,如果取到的值不再是&&,则可以认为这时候key有值了,从而避免了透传到数据库,从而把大量的类似请求挡在了缓存之中。

二、缓存并发

有时候如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的问题。

我现在的想法是对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询。

这种情况和刚才说的预先设定值问题有些类似,只不过利用锁的方式,会造成部分请求等待。

三、缓存失效

引起这个问题的主要原因还是高并发的时候,平时我们设定一个缓存的过期时间时,可能有一些会设置1分钟啊,5分钟这些,并发很高时可能会出在某一个时间同时生成了很多的缓存,并且过期时间都一样,这个时候就可能引发一当过期时间到后,这些缓存同时失效,请求全部转发到DB,DB可能会压力过重。

那如何解决这些问题呢?
其中的一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

我们讨论的第二个问题时针对同一个缓存,第三个问题时针对很多缓存。

总结来看:

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

2、缓存失效:如果缓存集中在一段时间内失效,DB的压力凸显。这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。
当发生大量的缓存穿透,例如对某个失效的缓存的大并发访问就造成了缓存雪崩。

四、大家提问汇总

1、问题1:
如何解决DB和缓存一致性问题?
答:当修改了数据库后,有没有及时修改缓存。这种问题,以前有过实践,修改数据库成功,而修改缓存失败的情况,最主要就是缓存服务器挂了。而因为网络问题引起的没有及时更新,可以通过重试机制来解决。而缓存服务器挂了,请求首先自然也就无法到达,从而直接访问到数据库。那么我们在修改数据库后,无法修改缓存,这时候可以将这条数据放到数据库中,同时启动一个异步任务定时去检测缓存服务器是否连接成功,一旦连接成功则从数据库中按顺序取出修改数据,依次进行缓存最新值的修改。

2、问题2:
问下缓存穿透那块!例如,一个用户查询文章,通过ID查询,按照之前说的,是将缓存的KEY预先设置一个值,,如果通过ID插过来,发现是预先设定的一个值,比如说是“&&”,那之后的继续等待访问是什么意思,这个ID什么时候会真正被附上用户所需要的值呢?
答:我刚说的主要是咱们常用的后面配置,前台获取的场景。前台无法获取相应的key,则等待,或者放弃。当在后台配置界面上配置了相关key和value之后,那么以前的key &&也自然会被替换掉。你说的那种情况,自然也应该会有一个进程会在某一个时刻,在缓存中设置这个ID,再有新的请求到达的时候,就会获取到最新的ID和value。

3、问题3:
其实用redis的话,那天看到一个不错的例子,双key,有一个当时生成的一个附属key来标识数据修改到期时间,然后快到的时候去重新加载数据,如果觉得key多可以把结束时间放到主key中,附属key起到锁的功能。
答:这种方案,之前我们实践过。这种方案会产生双份数据,而且需要同时控制附属key与key之间的关系,操作上有一定复杂度。

4、问题4:
多级缓存是什么概念呢?
答:多级缓存就像我今天之前给大家发的文章里面提到了,将ehcache与redis做二级缓存,就像我之前写的文章 http://www.jianshu.com/p/2cd6ad416a5a 提到过的。但同样会存在一致性问题,如果我们需要强一致性的话,缓存与数据库同步是会存在时间差的,所以我们在具体开发的过程中,一定要根据场景来具体分析,二级缓存更多的解决是,缓存穿透与程序的健壮性,当集中式缓存出现问题的时候,我们的应用能够继续运行。

 转载自 并发编程网 - ifeve.com

时间: 2024-09-02 16:59:30

缓存穿透、缓存并发、缓存失效之思路变迁缓存穿透、缓存并发、缓存失效之思路变迁的相关文章

《C++并发编程实战》——1.1 什么是并发

1.1 什么是并发 在最简单和最基本的层面,并发是指两个或更多独立的活动同时发生.并发在生活中随处可见.我们可以一边走路一边说话,也可以两只手同时做不同的动作,还有我们每个人都相互独立地过我们的生活--我在游泳的时候你可以看球赛,等等. 1.1.1 计算机系统中的并发 当我们提到计算机术语的"并发",指的是在单个系统里同时执行多个独立的活动,而不是顺序地或是一个接一个地.这并不是一种新的现象,多任务操作系统通过任务切换允许一台计算机在同一时间运行多个应用程序已司空见惯多年,一些高端的多

web服务器有自己的缓存吗?????除了http协议上规定的缓存。。。

问题描述 is对于图片有没有服务器端的缓存,,这里感觉http协议的缓存都已经排除了,,为什么请求web服务器他返回的还是说StatusCode:304NotModified(fromcache)如图所示:客户端缓存时间为0啊,所以按照http协议里说的,服务器端要对比If-Modified-Since和文件的修改时间的啊...明明请求的If-Modified-Since:Tue,01Mar201603:47:17GMT是老文件的修改时间了,,新的文件修改时间和这个不一样,,,为啥web服务器还

ConcurrencyMode.Multiple 模式下的WCF服务就一定是并发执行的吗:探讨同步上下文对并发的影响[下篇]

在<上篇>中,我通过一个具体的实例演示了WCF服务宿主的同步上下文对并发的影响,并简单地介绍了同步上下文是什么东东,以及同步上下文在多线程中的应用.那么,同步上下文在WCF并发体系的内部是如何影响服务操作的执行的呢?这实际上涉及到WCF的一个话题,即线程的亲和性(Thread Affinity),本篇文章将为你剖析WCF线程亲和机制的本质. 一.WCF线程亲和性(Thread Affinity) 对于服务端来说,WCF消息监听和接收体系通过IO线程池并发的处理来自客户端的服务调用请求,所以并发

ConcurrencyMode.Multiple模式下的WCF服务就一定是并发执行的吗:探讨同步上下文对并发的影响[上篇]

在<并发与实例上下文模式>中,我们通过实例演示的方式讲述了基于不同实例上下文模式的并发行为.对于这个实例中的服务类型CalculatorService,读者应该还记得我们对它进行了特别的定义:通过ServiceBehaviorAttribute特性将属性将UseSynchronizationContext设置成False.至于为何要这么做,这就是本篇文章需要为你讲述的内容.为了让读者对本节介绍的内容有一个深刻的认识,我们不然去掉ServiceBehaviorAttribute特性的UseSyn

测试并发应用(七)配置Eclipse来调试并发代码

声明:本文是< Java 7 Concurrency Cookbook>的第八章, 作者: Javier Fernández González 译者:郑玉婷 校对:方腾飞 配置Eclipse来调试并发代码 当今,几乎全部的程序员,不管使用什么程序语言,都会用IDE来创建应用.在相同的应用里,他们提供了很多有趣的功能来整合,例如: 任务管理 自动生成代码 自动生成文档 与版本控制系统集成 debugger测试应用 创建不同类型的应用和项目 IDE最有用的特点其一就是debugger.你可以一步一

测试并发应用(八)配置NetBeans来调试并发代码

配置NetBeans来调试并发代码 在当今世界,软件开发的应用必须工作正常,要达到公司的质量标准,还要在将来可以很容易的修改,而且不仅要在有限的时间内,还要尽可能低的费用支出.为了到达这个目标,必需使用 IDE,它集合了一个公共接口和多个工具(编译器和调试器)为了方便应用程序的开发. 如果你使用 Java 编程语言,那么 NetBeans 是最普遍的 IDE之一.它有一个内置调试器(integrated debugger)允许你调试你的应用.在这个指南,你将学习如何改变配置来帮助测试并发应用.

《C++并发编程实战》——1.2 为什么使用并发

1.2 为什么使用并发 在应用程序中使用并发的原因主要有两个:关注点分离和性能.事实上,我甚至可以说它们差不多是使用并发的唯一原因:当你观察得足够仔细时,一切其他因素都可以归结到这两者之一(或者可能是二者兼有,当然,除了像"我愿意"这样的原因之外). 1.2.1 为了划分关注点而使用并发 在编写软件时,划分关注点总是个好主意.通过将相关的代码放在一起并将无关的代码分开,这种方法可以使你的程序更容易理解和测试,从而减少出错的可能性.你可以使用并发来分隔不同的功能区域,即使在这些不同功能区

《Java并发编程的艺术》一一第1章并发编程的挑战

第1章并发编程的挑战 并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最大限度地并发执行.在进行并发编程时,如果希望通过多线程执行任务让程序运行得更快,会面临非常多的挑战,比如上下文切换的问题.死锁的问题,以及受限于硬件和软件的资源限制问题,本章会介绍几种并发编程的挑战以及解决方案.

缓存穿透、缓存并发、缓存失效之思路变迁

我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透     注: 上面三个图会有什么问题呢? 我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回.这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了. 那这种问题有什么好办法解决呢? 要是有人利用不存在的key频