BT

您是否属于早期采用者或者创新人士?InfoQ正在努力为您设计更多新功能。了解更多

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

| 作者 汪洋 发布于 2012年5月30日. 估计阅读时间: 不到一分钟 | 顶尖技术领导者的管理实战经验分享,尽在GTLC!

编者按:由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)关注我们,并与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

写的不错! by Gu Star

如题

少了一段程序的截图,后面明显的排版错误,图和文章对应不上。 by Lv Yong

也不知道编辑是不是仔细看了文章和配图

关于 及时的更新或者删除再缓存中已经过期或者失效的数据 的建议 by 邓 志勇

文章不错,但关键的 及时的更新或者删除再缓存中已经过期或者失效的数据 言之不详。
我们的处理思路是:使用 图 的数据结构来记录缓存项及其上游源数据的关系,每个缓存项在建立时,就要找到它在这个缓存项关系图中的位置,并且捕捉事件。当缓存关系图中任何一个节点数据有变化时,缓存管理程序会按照图的深度优先方式遍历相关节点,即清除这些相关节点的缓存项,通过这样可以实现精确的缓存项数据更新。

Re: 少了一段程序的截图,后面明显的排版错误,图和文章对应不上。 by Shui Arthur

抱歉,已经修改,谢谢您的反馈~:)

学习 by white bonnibell

缓存机制用的很少,学习中...

很有用 by 黄 旭栋

很有用,赞一下,多数遇到过,比如分页。

不咋地啊 by huo qikun

每种对象以hashtable保存,对每种查询条件自建索引,索引数据是对象的唯一标识。

这些可能应该属于基本知识了吧 by Qiang Mr

对高级程序员而言,如果这些基本知识都不清楚的话,不可能写出高性能的代码的,不过对于初学的读者们应该很有帮助

缓存应用程序的配置信息 by 吴 佳钊

缓存应用程序的配置信息
中的最后的配置信息不还是缓存在内存中么, 只不过是本地缓存.

挺好的 by 温 学谦

不错,学习学习

Re: 关于 及时的更新或者删除再缓存中已经过期或者失效的数据 的建议 by 李 军

很赞成的你的说法。这个部分作者一带而过。
根据你的思路我有些疑问确认下:如果你使用图的话,你这部分的缓存是不是就常驻内存(或分布式内存)了。不如不是的话,一旦缓存过期,你们是怎么处理的呢?

Re: 关于 及时的更新或者删除再缓存中已经过期或者失效的数据 的建议 by 李 军

还有就是,数据可能分布在几块,那你这个图应该就是非联通的图了?具体可以详述吗?期待交流.

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

12 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT