`

使用缓存的9大误区(下)

 
阅读更多

编者按:由InfoQ主办的全球架构师峰会将于2012年8月10日-12日在深圳举行,为了更好地诠释架构的意义、方法和实践,InfoQ中文站近期会集中发布一批与架构相关的文章,本篇即为其中之一。InfoQ也欢迎读者亲身参与到本次全球架构师峰会中,与来自国内外的顶尖架构师进行面对面的交流。报名参会请点击这里

本篇文章在上篇的基础上继续讨论了使用缓存的几个误区,包括:缓存大量的数据集合,而读取其中一部分;缓存大量具有图结构的对象导致内存浪费;缓存应用程序的配置信息;使用很多不同的键指向相同的缓存项;没有及时的更新或者删除再缓存中已经过期或者失效的数据。

缓存大量的数据集合,而读取其中一部分

在很多时候,我们往往会缓存一个对象的集合,但是,我们在读取的时候,只是每次读取其中一部分。 我们举个例子来说明这个问题(例子可能不是很恰当,但是足以说明问题)。

 

在购物站点中,常见的操作就是查询一些产品的信息,这个时候,如果用户输入了“25寸电视机”,然后查找相关的产品。这个时候,在后台,我们可以查询数据库,找到几百条这样的数据,然后,我们将这几百条数据作为一个缓存项缓存起来,代码的代码如下:

同时,我们对找出的产品进行分页的显示,每次展示10条。其实在每次分页的时候,我们都是根据缓存的键去获取数据,然后选择下一个10条数据,然后显示。

如果是使用本地内存缓存,那么这可能不是什么问题,如果是采用分布式缓存,问题就来了。下图可以清楚的说明这个过程,如图所示:

相信大家看完这个图,然后结合之前的讲述应该很清楚了问题所在了:每次都按照缓存键获取全部数据,然后在应用服务器那里反序列化全部数据,但是只是取其中10条。

这里可以将数据集合再次拆分,分为例如25-0-10-products,25-11-20-products等的缓存项,如下图所示:

 

当然,查询和缓存的方式有很多,拆分的方式也有很多,这里这是给出一些常见的问题!

缓存大量具有图结构的对象导致内存浪费

为了更好的说明这个问题,我们首先看到下面的一个类结构图,如图:

如果我们要把一些Customer数据缓存起来,这里就可以可能出现两个问题:

  1. 由于使用.NET的默认序列化机制,或者没有适当的加入相应Attribute(属性),使得缓存了一些原本不需要缓存的数据。

  2. 将Customer缓存的时候,同时,为了更快的获取Customer的Order信息,将Order信息缓存在了另外一个缓存项中,导致同一份数据被缓存两次。

下面,我们就分别来看看这两个问题。

首先看到第一个。如果我们使用分布式缓存来缓存一些Customer的信息的时候,如果我们没有自己重新Customer的序列化机制,而是采用的默认的,那么序列化机制在序列化Customer的时候,会将Customer所引用的对象也序列化,然后在序列化被序列化对象中的其他引用对象,最后的结果就是:Customer被序列化,Customer的Order信息被序列化,Order引用的OrderItem被序列化,最后OrderItem引用的Product也会序列化。

整个对象图全部被序列化了,如果这种情况是我们想要的,那么没有问题;如果不是的,那么,我们就浪费了很多的资源了,解决的方法有两个:第一,自己实现序列化,自己完全控制哪些对象需要序列化,我们前面已经讲过了;第二,如果使用默认的序列化机制,那么在不要需要序列化的对象上面加上[NonSerialized]标记。

下面,我们看到第二个问题。这个问题主要是由于第一个问题引起的:原本在缓存Customer的时候,已经将Customer的其他信息,例如Order,Product已经缓存了。但是很多的技术人员不清楚这一点,然后又把Customer的Order信息去缓存在其他的缓存项,使用的使用就根据Customer的标识,例如ID去缓存中获取Order信息,如下代码所示:

解决这个问题的方法也比较明显,参看第一个问题的解决方案就可以了!

缓存应用程序的配置信息

因为缓存是有一套数据失效检测周期的(之前说过,要么是固定时间失效,要么是相对时间失效),所以,很多的技术人员喜欢把一些动态变化的信息保存在缓存中,以充分利用缓存机制的这种特性,其中,缓存程序的配置信息就是其中一个例子。

因为在应用的中的一些配置,可能会发生变化,最简单的就是数据库连接字符串了,如下代码:

当这样设置之后,每隔一段时间缓存失效之后,就去重新读取配置文件,这时候,可能此时的配置就和之前不一样了,并且其他的地方都可以读取缓存从而进行更新,特别是在多台服务器上面部署同一个站点的时候,有时候,我们没有及时的去修改每个服务器上面的站点的配置文件里面的信息,这个时候如何使用分布式缓存缓存配置信息,只要更新一个站点的配置文件,其他站点就全部修改了,技术人员皆大欢喜。OK,这确实看起来是个不错的方法(在必要的时候可以采用一下),但是,不是所有的配置信息都要保持一样的,而且还要考虑怎样一个情况:如果缓存服务器出了问题,宕机了,那么我们所有使用这个配置信息的站点可能都会出问题。

建议对于这些配置文件的信息,采用监控的机制,例如文件监控,每次文件发生变化,就重新加载配置信息。

使用很多不同的键指向相同的缓存项

我们有时候会遇到这样的一个情况:我们把一个对象缓存起来,用一个键作为缓存键来获取这个数据,之后,我们又通过一个索引作为缓存键来获取这个数据,如下代码所示:

我们之所以这样写,主要因为我们会以多种方式来从缓存中读取数据,例如在进行循环遍历的时候,需要通过索引来获取数据,例如index++等,而有些情况,我们可能需要通过其他的方式,例如,产品名来获取产品的信息。

如果遇到这样的情况,那么就建议将这些多个键组合起来,形成如下的形式:

另外一个常见的问题就是:相同的数据被缓存在不同的缓存项中,例如,如果用户查询尺寸为36寸的彩电,那么可能有可能一个编号为100的电视产品就在结果中,此时,我们将结果缓存。另外,用户在查找一个生产厂家为TCL的电视,如果编号为100的电视产品又出现在结果中,我们把结果又缓存在另外一个缓存项中。这个时候,很显然,出现了内存的浪费。

对于这样的情况,之前笔者采用的方法就是,在缓存中创建了一个索引列表,如图所示:

当然,这其中有很多的细节和问题需要解决,这里就不一一述说,要看各自的应用和情况而定! 也非常欢迎大家提供更好的方法。

没有及时的更新或者删除再缓存中已经过期或者失效的数据

这种情况应该是使用缓存最常见的问题,例如,如果我们现在获取了一个Customer的所有没有处理的订单的信息,然后缓存起来,类似的代码如下:

之后,用户的一个订单被处理了,但是缓存还没有更新,那么这个时候,缓存中的数据就已经有问题!当然,我这里只是列举的最简单的场景,大家可以联想自己应用中的其他产品,很有可能会出现缓存中的数据和实际数据库中的不一样。

现在很多的时候,我们已经容忍了这种短时间的不一致的情况。其实对于这种情况,没有非常完美的解决方案,如果要做,倒是可以实现,例如每次修改或者删除一个数据,就去遍历缓存中的所有数据,然后进行操作,但是这样往往得不偿失。另外一个折中的方法就是,判断数据的变化周期,然后尽可能的将缓存的时间变短一点。

关于作者

汪洋,现任惠普架构师、信息分析师《NET应用架构设计:模式、原则与实践》作者。上海益思研发管理咨询有限公司首席软件架构专家,软件咨询组副组长。


感谢崔康对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

分享到:
评论

相关推荐

    使用缓存的9大误区(上)

    如果说要对一个站点或者应用程序经常优化,可以说缓存的使用是最快也是效果最明显的方式。一般而言,我们会把一些常用的,或者需要花费...在本篇文章中,我们将为大家讲述导致以上问题的9大症结,并且给出相对应的解

    【JavaScript源代码】vue keepAlive缓存清除问题案例详解.docx

    另一个常见误区是尝试使用`vm.$destroy()`方法来销毁组件以清除缓存。虽然这确实可以销毁组件,但同时也意味着该组件将无法再次被缓存,因为它已经被完全移除了。因此,一旦调用了`$destroy()`,该组件将永久失去...

    PCB设计之:必知的PCB设计八大误区

    以下是根据提供的内容总结的八大PCB设计误区及其详细解释: 1. 误区一:选择细线宽进行自动布线 自动布线虽然方便快捷,但过细的线宽会增加PCB面积,同时产生大量过孔,增加了制造成本。在批量生产中,减少线宽和过...

    电路设计中的八大误区及点评

    软件可以通过减少外存访问次数、使用寄存器变量、内部缓存等方式来减少功耗。此外,及时响应中断等措施也能有效降低功耗。因此,降低功耗是软硬件工程师共同的责任。 以上误区及点评涵盖了电路设计中常见的问题,...

    浅谈网站架构中缓存的应用

    ### 缓存的基本知识 #### 缓存的由来与定义 缓存技术源于不同层级存储设备之间的速度差异。...合理设计和使用缓存不仅能够显著提升系统的性能,还能有效降低数据库负载,从而带来更佳的用户体验。

    Python技术使用误区分析.docx

    - **利用缓存:** 对于重复的计算结果,可以使用缓存机制来避免重复计算。 - **异步处理:** 对于耗时较长的任务,可以采用异步处理的方式来提高效率。 **总结:** 避免以上误区是提高Python开发效率和代码质量的...

    工程师在电路设计中的八个误区

    在电路设计过程中,电子工程师可能会存在一些认识上的误区,以下便是从提供的文件内容中提炼出的八大常见误区,并对其进行了详细的解析: 误区一:PCB设计要求不高,使用细线自动布线 在设计印刷电路板(PCB)时,...

    混合存储中Flashcache使用的误区以及解决方案1

    默认情况下,这个参数设置为0,意味着所有I/O操作(包括顺序和随机)都会被缓存。当设置为非零值时,一旦检测到超过指定大小的顺序I/O,Flashcache将在跳过剩余部分之前缓存一部分数据。 正确理解和调整这个参数...

    CPU选购误区.pdf

    微架构、频率、缓存大小和技术优化等都会影响性能。只有在其他条件相近时,核心数多的CPU才可能更强。用户应根据实际需求,如多任务处理或专业应用,来选择合适的CPU。 第五,CPU的频率虽重要,但并非全部。频率是...

    经验总结:电路设计的误区 2

    - 减少外部存储器的访问次数:通过使用更多的寄存器变量或利用内部缓存等方式,可以显著减少对外部存储器的访问次数,从而降低功耗。 - 及时响应中断:中断处理通常是低电平有效的,并带有上拉电阻。通过优化中断...

    提高Oracle数据库性能的四个误区

    使用RAC也可能加重性能负担,因为它需要协调全局高速缓存一致性,加剧高速缓存争用、I/O和锁定问题。 在优化Oracle数据库性能时,首先需要正确分析应用特性。根据描述,应用主要为联机事务处理(OLTP),并包含少量...

    python模块调用误区.docx

    然而,在使用Python模块时,开发者常常会遇到一些误区,这可能导致代码的混乱和不易维护。本文将详细探讨这些误区以及如何正确地调用模块。 首先,我们来谈谈一个常见的错误做法:“`from module import *`”。这种...

    必知的电路设计八大误区

    本文将详细分析和解释电路设计中常见的八大误区,以帮助工程师们避免这些陷阱,实现更高效、更节能的设计。 误区一:自动布线使用细线。在快速发展的电子工程领域,自动布线工具因其能够节省时间而被广泛应用。但是...

    Zabbix 布置时的常见五个误区.docx

    正确的做法是观察Zabbix内部进程、数据收集和缓存使用情况,根据实际需求动态调整。 3. 使用模板功能: 模板在Zabbix中的作用是提高效率和管理便利性。一开始,用户可能会倾向于直接在主机上创建监控项和触发器,...

    经验分享:工程师在电路设计中的八大误区

    以下是对标题和描述中提及的八大误区的详细解析: 1. **误区一:自动布线优先**:一些工程师认为细线和自动布线可以节省成本,但事实上,自动布线可能导致更大的PCB面积和过多的过孔,增加生产成本。手动布线在某些...

    网站架构技术

    使用缓存改善网站性能 缓存类型 本地缓存 分布式缓存 缓存产品 redis 业界主流 memcached 解决问题 数据库访问 使用应用服务器集群改善网站的并发处理能力 问题: 负载均衡...

    如何正确查看Linux机器内存使用情况.docx

    然而,`top`命令显示的内存使用情况可能会让人误解,因为它通常将缓存内存视为已使用的内存。实际上,这部分内存是可快速释放给其他进程使用的,因此,单纯的`top`输出并不完全反映实际可用内存。 3. **`free`命令...

    电路设计的几个误区需要注意

    多数情况下,适度过冲不会影响信号质量,而且匹配电阻过大可能导致功耗增加。应根据信号类型和标准来决定是否需要匹配以及匹配的程度。 8. 软件与硬件功耗关系的认识误区:降低功耗不仅需要硬件设计的优化,软件...

    进击的Golang_209实用知识库分享

    在Golang使用技巧方面,本资源分享了-channel的使用坑、计时器的使用误区等问题的解决方法。 Go runtime 在Go runtime方面,本资源分享了runtime的相关知识,例如GC与内存分配的优化。 代码审查和质量评估 在...

Global site tag (gtag.js) - Google Analytics