论坛首页 Java企业应用论坛

Cache的选择以及特性建议

浏览 20612 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-08-11  
布娃娃(buaawhl:P)的cache insight非常好,引发了我一点小小的思考。因为讨论方向似乎略有不同,所以就另开新贴了。

clustercache,优势在于同一个jvm中local的get和put。多机环境下,它是非singleton的cache,remove时有同步需求,需要通讯。此外,在容量上,因为get和put发生在本地,那么当内存不够的时候,cache在本地的文件系统也是可以接受的处理方式。部署上,无需单独的cacheserver,每个jvm中自带cache能力,成本似乎低一点。

centercache,尤以memcached为代表,无论做不做hash,在逻辑上都可以认为它是singleton的cache。如果cache的是关键数据,那么在failover时可能就会有问题(后面的例子会提到)。容量上,因为get和put本来就是remote的,如果把内容放到文件系统中,那么相比clustercache,在性能上可能就没有什么优势了(就是说,memcached就是inmemory的,就不用考虑放到filesystem了)。centercache虽说没有部署上的限制,但从优化的角度来说,似乎最好要有一台独立的cache服务器才能发挥最大优势。

技术服务于应用,脱离了应用去谈技术多少有纸上谈兵的感觉。比如,Robbin提到的用memcached来存session,就要区分不同的情况。如果是个购物网站,把用户的购物车(用户已经选择但还没有结帐的那些商品)数据保存在session中(当然还有其它的实现方式,先这么假设好了),那么session的丢失就是不能接受的,也就是说,要重起memcachedserver?最好偷偷摸摸等到半夜。对于这种情况,我们通常就不用cache机制,而是直接使用session的文件方式,即使重起也不会有太大的影响。当然,如果应用的session中没有这样的“敏感数据”,丢了就算,那就是另外一回事了。

需要强调的一点是:从应用的角度,即使是在一个项目之中,也有同时使用两种cache的可能,两者之间并不是非此即彼的。

我觉得clustercache比较适合那些“经典的cache”使用场景,比如,页面某个区域的缓存,ORM中用到的cache等,因为可能一个页面或者一个功能可能就需要n多次的调用,还是local的比较让人放心。而且,如果需要cache的数据量巨大,比如要搞个十万二十万的,也可以放心的配置为使用文件cache,而不用担心吃掉太多内存,或者命中率降低太多。

适合centercache的是一些比较“需求比较BT”的“非关键数据”场景,比如,要显示“当前用户的状态”,全站范围,一个用户只有一个当前状态,毫无疑问是singleton的,这就最适合用centercache了。此外,比如要显示“某某东西的点击次数”,这种貌似singleton和丢了也不心疼的东西,统统可以拖近来。

简而言之就是一句话:用合适的方法来干合适的事情,咱们要扬长避短以及看菜吃饭。

to:buaawhl 关于 cluster cache 的一点特性建议

知道你正在实现cache,所以提出一个特性供你参考。

本来,cluster cache干好自己的本职工作就行了,老老实实的做自己的local的get和put,在remove的时候broadcast一下,这就是它的一份很有前途的本职工作。至于对象之间的关联,外层代码告诉我要remove掉哪个我就remove掉哪个,删除一个对象的时候,你有多个对象也需要remove?没问题,不过那是你的职责,你多调几次remove就好了。每一个我都老老实实的broadcast。这似乎就使cluster cache的现状。

这么做,当然work,不过似乎还不够好。问题的关键在于两点,第一,对于外层代码来说,本来应该自己维护cache的依赖关系,可是这件事要我“逐个通知”,似乎还是有点繁琐,最好能有人帮我代劳,或者其他对象监听我发布的事件。第二,如果依赖关系复杂,remove一个东西可能触发一场remove风暴,而这么多个remove都要在网络上面跑来跑去,虽说remove的消息本身很小,但这么干似乎还是很要命。

那么,能否建立一个remove的依赖关系呢?

外层代码只需要remove自己关注的对象,这条remove消息broadcast,接受到的其他cache根据这个remove消息,检查依赖关系,分别去localremove其他关联对象。这么做的好处就是解决上面两个问题,而代价就是:A,需要一个remove依赖关系的配置,B,需要对key进行一番设计。其中的A比较容易理解,那么B主要是将那些会影响“关联cache”的因素整合到cache的key上来。这样的好处是,cache无需理解和遍历所缓存的对象,坏处是带来了一些复杂性。不过,既然将“通知清除其他的cache”这么繁琐的任务都移出去了,那这点麻烦应该还是可以接受的。

具体的实现方式就很多了,比如,对复合的key进行正则表达式匹配,符合就删,不符合就留着,示意如下:
<!-- key=xxx/yyy -->
<cache region="post">
</cache>

<!-- key=xxx -->
<cache region="thread">
	<depends region="post" match="${(*.);/*}"/>
</cache>


以上纯建议,供你纯参考。
   发表时间:2006-08-11  
我不知道大家为什么对cache server的failover那么敏感。一个极其稳定,而且消耗CPU资源可以忽略不计的服务,考虑它failover的意义何在呢?

那为什么大家从来不去考虑一下交换机fail的可能性,考虑一下路由器OS的fail的可能行呢?至少我就遇到过Cisco OS fail掉的情况,这种情况下,你后面服务再robust,再failover,有啥用呢?就好像你每天都在盘算出门被车撞了怎么办一样,实际上这种事情概率太小了,小到你根本不需要考虑它,还不如多考虑考虑每天上班迟到的可能性呢。

我的例子可能不恰当,就是想提醒大家一下,在真实应用环境下,很多问题不像想像中的样子。其实分布式Cache的复杂性带来的不稳定性更加容易导致各种问题。
0 请登录后投票
   发表时间:2006-08-11  
thanks Jackyz.

remove的通信粒度确实应该增大。我现在提供了remove keys。
你提出的这个pattern match也是一个很好的思路。
更进一步,可以提供一个query。remove query. 删除所有符合条件的数据。

这个 query dsl 甚至可以采用script。比如采用ruby 实现一个cache,可以传播出去一段ruby 代码,用来清除各个node的cache。
0 请登录后投票
   发表时间:2006-08-11  
robbin 写道
我不知道大家为什么对cache server的failover那么敏感。一个极其稳定,而且消耗CPU资源可以忽略不计的服务,考虑它failover的意义何在呢?

那为什么大家从来不去考虑一下交换机fail的可能性,考虑一下路由器OS的fail的可能行呢?至少我就遇到过Cisco OS fail掉的情况,这种情况下,你后面服务再robust,再failover,有啥用呢?就好像你每天都在盘算出门被车撞了怎么办一样,实际上这种事情概率太小了,小到你根本不需要考虑它,还不如多考虑考虑每天上班迟到的可能性呢。

我的例子可能不恰当,就是想提醒大家一下,在真实应用环境下,很多问题不像想像中的样子。其实分布式Cache的复杂性带来的不稳定性更加容易导致各种问题。


大有Martin Fowler所说的“分布式系统的设计原则的第一条是:不要使用分布式”。
0 请登录后投票
   发表时间:2006-08-11  
to Jackyz
不是很明白,你说的clustercache的现状。
按照我的理解clustercache的广播事件,同在单台服务器上处理的情况,完全是一样的。因为它们的结构和配置完全一样。不存在,外层代码需要知道该清除那些cache的问题,实际上,外层代码都不知道cache了那些对象!
0 请登录后投票
   发表时间:2006-08-11  
buaawhl, 周末上msn来讨论一下问题吧。
0 请登录后投票
   发表时间:2006-08-12  
购物车应用一样可以将productId set等关键信息放在cookie中,服务端使用memcached集中存储,不用担心。
0 请登录后投票
   发表时间:2006-08-14  
sorphi 写道
购物车应用一样可以将productId set等关键信息放在cookie中,服务端使用memcached集中存储,不用担心。


jackyz 写道
如果是个购物网站,把用户的购物车(用户已经选择但还没有结帐的那些商品)数据保存在session中(当然还有其它的实现方式,先这么假设好了)
0 请登录后投票
   发表时间:2006-08-14  
jackyz 写道
partech 写道
不是很明白,你说的clustercache的现状。
按照我的理解clustercache的广播事件,同在单台服务器上处理的情况,完全是一样的。因为它们的结构和配置完全一样。不存在,外层代码需要知道该清除那些cache的问题,实际上,外层代码都不知道cache了那些对象!

我这里说的外层代码是指那些会直接调用cache api的代码。更外层的代码(比如使用hibernate api的代码)也许不知道cache,但直接在cache之上的代码(比如hibernate内部处理cache逻辑的代码)是需要知道cache的。这个层次的代码,会在某个地方调用cache.put(key,obj);也必然需要在另外的地方调用cache.remove(key)。

对这层次的代码来说,我上面描述的问题其实是一个rpc通讯量的问题。这就好比ejb中local和remote的方法调用。比如,a对象依赖于b对象,当b对象改变时,需要cache.remove(b), cache.remove(a),在cluster场景,这就是两个rpc。而如果每个cluster上都有一个对象的依赖关系配置,那么就可以做到在改变b对象时,只调用cache.remove(b),在这个必须的rpc之后,各个cluster可以自己根据依赖关系,以本地的方式cache.localremove(a)。

上面的场景中,依赖关系非常简单,只节省了一次rpc,而如果依赖关系很复杂,比如,更新1个对象需要更新N*1个关联对象。那就可以节省出更多的rpc,这是通讯上的好处。

而在代码方面,对于“需要关注cache”的代码,这么做的好处是解耦了“通知关联对象remove cache”的职责,它只需要关注自己的更新,代码可以更为清晰。

此外,我想了一下,对于center的cache也同样存在这个问题,它的remove也是rpc操作,如果没有这个“配置的依赖关系”,要更新关联的对象,同样也需要n次rpc的remove操作。

基于封装的考虑,我认为,直接调用cache api的代码,就不该通过多次调用remove来控制,仅仅需要调用remove(a),就行了,至于是否还需要remove其他的东西,那是Cache需要考虑的。
保证缓存的数据不会出现脏读,本来就该属于cache自己的职责。
0 请登录后投票
   发表时间:2006-08-14  
引用
把用户的购物车(用户已经选择但还没有结帐的那些商品)数据保存在session中(当然还有其它的实现方式,先这么假设好了),那么session的丢失就是不能接受的


无论其他实现方式是什么,反正不是放在cookie中这种方式,不然怎么就不可接受呢?咬文嚼字啊你
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics