基于EHCache实现缓存去重

Posted on

基于EHCache实现缓存去重 - passover【毕成功的博客】 - 51CTO技术博客

分享到

百度分享

51CTO首页51CTO博客我的博客 搜索 每日博报

社区:论坛博客下载读书更多

登录注册

[

passover【毕成功的博客】

](http://passover.blog.51cto.com/)

http://passover.blog.51cto.com 【复制】 【订阅】

原创:94翻译:2转载:21

博 客|图库|写博文|帮 助

passover 的BLOG

头像升级了?来看看>> 写留言邀请进圈子发消息 加友情链接进家园 加好友

博客统计信息

51CTO推荐博客 用户名:passover 文章数:117 评论数:174 访问量:162194 无忧币:1682 博客积分:2780 博客等级:7 注册日期:2010-11-16 距离博客No.1争夺赛结束还有 10 天

热门文章

搜索BLOG文章

我的技术圈(2)

更多>>

最近访客

yyxxxn

vnet111

vbbb625

naxxmm

mumab..

wangj..

hjdon..

睡着了

李惟忠

李大玉

51cto..

glories

最新评论

51CTO推荐博文

更多>>

2012年10月MVP名单公布啦!周刊:从需求看《IT人员应聘建议》【有奖门诊】探索式测试的奥秘

博主的更多文章>>

基于EHCache实现缓存去重 2011-01-27 16:15:10 标签:缓存 职场 休闲 ehcache

 由于近期的工作主要集中在数据处理上,而性能问题时而暴露出来,我对需要处理的数据进行了一下简单的分析,发现存在大量的重复数据,这自然让我想到了去建立一个二级缓存把曾经处理过的数据缓存起来,避免重复处理。我们业务上其实就是对最近处理过的数据重复出现几率比较高,所以有一个几百兆的内存空间用LRU的策略进行去重应该就足够了。

其实可以选择的方案有很多,初步筛选了一下,我决定在对Java支持度比较好且应用广泛的OSCache和EHCache中选一个。上了官网一查,发现OSCache在几年前就停止更新了,而EHCache则一直有公司在维护,所以自然选定了后者。官方文档的地址是[http://www.ehcache.org/documentation/index.html](http://www.ehcache.org/documentation/index.html)。

先让同事研究了一下,弄的是1.7.0的版本,这个版本比较好的是对其他包的依赖很少,很快就把自己的demo建立起来了。然后上官网下载了最新的版本ehcache-core-2.3.1-distribution,里面其实就多了两个slf4j的包,原来的代码一行没动就可以运行起来了。这里新版本加入的内容比较多,jar包就是1.7.0的3倍大,看了下官方文档的说明,主要加入的就是对分布式的支持,当然还有很多新特性。新特性以后慢慢研究吧,我目前也用不到什么高级功能,既然新版本使用起来也很方便,那就用这个好了。

这里简单解释一下,我们原先想试一下他提供的FIFO和LRU的策略,结果刚开始测试输出的结果和预期居然不一致,官方文档上也没看到相应的解释。经过反复测试,感觉他不是严格按照这个策略来,可能是算法有些问题吧。

另外补充一下参数设置的经验。
  1. 对于存储对象个数的设置:由于配置文件中只能指定maxElementsInMemory,这就会有可能存入的对象太多而超出VM的heap大小,当然你可以通过jvm参数增大heap大小,但这总还是有可能溢出。这里可以把maxElementsInMemory值设置到一个比较安全的大小,自己预先测试一下最好。如果内存仍然存不下你需要存的对象个数,那么可以开启overflowToDisk来增加可以存储的Element个数。这里要注意一下,EHCache不会自动帮助你去把内存对象写入到磁盘,当超过maxElementsInMemory程序会自动把更多的部分开始往硬盘写,但是内存的对象其实并没有清出去,这时需要手动使用Cache.flush()方法来把内存对象清出去。 对于是否需要用磁盘扩充缓存,这个还是根据自己应用判断。
  2. 重建上一次运行的缓存:这个需求肯定比较普遍,我们当然不希望一旦程序退出,整个缓存就要重建了。开启diskPersistent功能,只要使用的是CacheManager单例模式,下一次启动的时候就会调用上一次运行的缓存。比较麻烦的是写入磁盘的时间还是要自己调用 Cache.flush()方法。如果仅仅考虑到程序重启的话,我建议这里把diskStore写入到一个ramfs,这样性能就更高了,但重启电脑的话就不得不重建缓存了。
  3. 索引的建立:如果你测试的maxElementsOnDisk量比较大的话,我本地设置成100000,那么你会在临时文件目录下看到有index文件,这个文件显然是对磁盘上的文件建立索引,加快查询速度。所以这个索引是否建立是EHCache自动帮你做的,你不用操心了。
  4. 写入缓存的速度:在本地不断调整参数,惊讶的发现有时写入缓存的速度奇慢。因为开启了 diskPersistent功能,程序运行时经常会卡在backOffIfDiskSpoolFull方法上,作用是If the disk store spool is full wait a short time to give it a chance to catch up。这个参数对应的是diskSpoolBufferSizeMB,默认是30MB,如果存储的对象比较多,强烈建议调大,或者直接把 diskPersistent关掉。
  5. 缓存内容写入磁盘的速度:如果你把maxElementsOnDisk参数配置的远小于maxElementsInMemory值的话,你会发现速度又变得很慢,这是因为程序一直要去找到底哪些内容应该写入磁盘。建议这 maxElementsOnDisk值的配置应该不小于 maxElementsInMemory,其实正常用应该也会这么配置,只是我测试无聊试了下出问题了。
  6. 内存Element数量不能控制:一旦开启了 diskPersistent,惊讶的发现居然设置的 maxElementsInMemory无效了,内存的 Element数量一直在增长。反复测试,问题稳定重现,又找不到解释,无奈之下给官方提了个bug,等答复吧,汗...
 这里还是给出测试代码,主要用到的就是一个ehcache.xml的配置文件,定义了Cache的具体策略。程序调用的时候非常方便,就是典型key/value的方式。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <defaultCache
  3. maxElementsInMemory="10000"
  4. eternal="false"
  5. timeToIdleSeconds="120"
  6. timeToLiveSeconds="120"
  7. overflowToDisk="true"
  8. diskPersistent="false"
  9. diskExpiryThreadIntervalSeconds="120"
  10. memoryStoreEvictionPolicy="LRU"
  11. />
  12. <cache name="sample"
  13. maxElementsInMemory="5"
  14. maxElementsOnDisk = "5"
  15. eternal="false"
  16. timeToIdleSeconds="1440"
  17. diskPersistent="false"
  18. timeToLiveSeconds="2880"
  19. overflowToDisk="false"
  20. memoryStoreEvictionPolicy="FIFO"
  21. statistics="true"
  22. />
  23. 以下就是测试代码:

  24. package ehcache;

  25. import org.apache.log4j.Logger;

  26. import net.sf.ehcache.Cache;
  27. import net.sf.ehcache.CacheManager;
  28. import net.sf.ehcache.Element;

  29. public class EhcacheTest {

  30. private static final Logger logger = Logger.getLogger(EhcacheTest.class);

  31. private static Cache sampleCache = null;

  32. public static void main(String[] args) {
  33. init();
  34. test();
  35. }

  36. private static void test() {
  37. logger.info(sampleCache.getMemoryStoreEvictionPolicy());
  38. for(int i=0; i<10; i++){
  39. //写入缓存
  40. sampleCache.put(new Element(i, "v" + i));
  41. //打印当前缓存的所有值
  42. logger.info(sampleCache.getKeys());
  43. //读取缓存
  44. Element e = sampleCache.get(i);
  45. logger.info(e.getValue());
  46. }
  47. //打印命中统计
  48. logger.info(sampleCache.getStatistics());
  49. }

  50. private static void init() {
  51. CacheManager manager = CacheManager.create();
  52. // manager.addCache("sample"); //已经在配置文件定义过了
  53. sampleCache = manager.getCache("sample");
  54. }

  55. }

分享至 ** 更多 0 一键收藏,随时查看,分享好友!

姜周、倔强的土豆 2人 了这篇文章

类别:Open Source技术圈(0)┆阅读(3391)┆评论(2) ┆ 推送到技术圈返回首页 上一篇 利用Jsoup解析HTML 下一篇 初试Redis的感受

相关文章

文章评论

[1楼] 姜周 回复

2011-01-28 14:44:20 支持一个!

[2楼] 倔强的土豆 回复

2012-08-24 17:55:22 不错,顶了。

发表评论

2013年1月MVP申请公告[截止10月15日] 昵 称: 登录 快速注册 验证码:

点击图片可刷新验证码请点击后输入验证码博客过2级,无需填写验证码 内 容:

同时赞一个 返回顶部

Copyright By 51CTO.COM 版权所有

希望本站内容对您有点用处,有什么疑问或建议请在后面留言评论
转载请注明作者(RobinChia)和出处 It so life ,请勿用于任何商业用途