【设计优化】-使用缓存(Cache)提高程序性能
来源:互联网 发布:网络剪刀 编辑:程序博客网 时间:2024/05/21 15:22
缓存(Cache)就是一块用来存放数据的内存空间。主要作用是暂存数据处理结果,并提供下次访问使用。
缓存的使用非常普遍,比如,浏览器都会在本地缓存页面,从而减少HTTP 的访问次数。又如服务器系统开发时,设计人员为一些核心的 API 加上缓存,从而提高系统的缓存时间。
最简单的缓存实现可以使用 HashMap 。当然,这样做会有很多问题,如何时清理无效的数据;如何防止缓存数据过多而导致内存溢出等。一个稍好的方案是使用WeakHashMap,使用弱引用维护一张哈希表,而且可以在内存不足的时候清理数据。
但是作为最专业的实现,就应该有一套专业的缓存框架。比如EHCache、OSCache 和 JBossCache 等。EHCache 缓存出自 Hibernate, 是 Hibernate 框架默认的数据缓存解决方案;OSCache 是由 OpenSympthony 设计的,可以用来缓存任何对象,甚至是缓存部分 JSP 页面或者 HTTP 请求;JBossCache 是由 JBoss 开发、可用于 JBoss 集群间数据共享的缓存框架。
下面,我们以 EHCache 为例,简单介绍一下缓存的基本使用方法。
首先,我们需要在官网 http://ehcache.org 下载 EHCache 包 ,下载完成之后,将 lib 文件夹下的 jar 包添加到工程中就可以使用了。
引入 jar 包之后,在工程的 Classpath 路径下新建一个名为 ehcache.xml 的文件,如上图所示,内容如下:
<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true"> <diskStore path="data/ehcache" /> <defaultCache maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap" /> </defaultCache> <cache name="sampleCache1" maxEntriesLocalHeap="10000" maxEntriesLocalDisk="1000" eternal="false" diskSpoolBufferSizeMB="20" timeToIdleSeconds="300" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LFU" transactionalMode="off"> <persistence strategy="localTempSwap" /> </cache> <cache name="sampleCache2" maxEntriesLocalHeap="1000" eternal="true" memoryStoreEvictionPolicy="FIFO" /></ehcache>
以上配置文件中首先配置了一个默认的 cache 模板。在程序中使用 EHCache 接口动态生成缓存的时候,会使用这些参数定义新的缓存。随后,定义了两个缓存, 名字分别为 sampleCache1 和 sampleCache2 。
配置文件中主要的参数及含义如下:
必要属性有3个,
maxEntriesLocalHeap:堆内存中最大缓存对象数,值为0则没有限制
maxEntriesLocalDisk:磁盘中的最大对象数,默认为0不限制
eternal:elements是否永久有效,如果为true,timeouts将被忽略,element将永不过期
以下是可选属性
timeToIdleSeconds:失效前的空闲秒数,当eternal为false时,这个属性才有效,0为不限制
timeToLiveSeconds:失效前的存活秒数,创建时间到失效时间的间隔为存活时间,当eternal为false时,这个属性才有效,0为不限制
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
clearOnFlush:当调用flush()是否清除缓存,默认是
memoryStoreEvictionPolicy:内存回收策略,默认回收策略:最近最少使用Least Recently Used,先进先出First In First Out,Less Frequently Used使用频率最低。localTempSwap 则在缓存数目多的时候交换到磁盘上。
timeToIdleSeconds:如果不是永久存储的缓存,那么在 timeToIdleSeconds 指定时间内没有访问一个条目,则移除它
diskPersistent: 磁盘中的条目是否永久保存
diskExpiryThreadIntervalSeconds:清理缓存的线程运行的时间间隔
transactionalMode:设置缓存动作的事务模式
然后我们在 Java 程序中使用预先定义的缓存。
package bupt.xiaoye.charpter2.ehcache;import net.sf.ehcache.Cache;import net.sf.ehcache.CacheManager;import net.sf.ehcache.Element;public class EhCacheTest { public static void main(String[] args) throws InterruptedException { CacheManager manager = CacheManager.create(); // 取出所有的cacheName String names[] = manager.getCacheNames(); System.out.println("----all cache names----"); for (int i = 0; i < names.length; i++) { System.out.println(names[i]); } System.out.println("----------------------"); // 得到一个cache对象 Cache cache1 = manager.getCache(names[0]); // 向cache1对象里添加缓存 cache1.put(new Element("key1", "values1")); Element element = cache1.get("key1"); // 读取缓存 System.out.println("key1 \t= " + element.getObjectValue()); // 手动创建一个cache(ehcache里必须有defaultCache存在,"test"可以换成任何值) Cache cache2 = new Cache("test", 1, true, false, 2, 3); manager.addCache(cache2); cache2.put(new Element("jimmy", "菩提树下的杨过")); // 故意停1.5秒,以验证是否过期 Thread.sleep(1500); Element eleJimmy = cache2.get("jimmy"); //1.5s < 2s 不会过期 if (eleJimmy != null) { System.out.println("jimmy \t= " + eleJimmy.getObjectValue()); } //再等上0.5s, 总时长:1.5 + 0.5 >= min(2,3),过期 Thread.sleep(500); eleJimmy = cache2.get("jimmy"); if (eleJimmy == null) { System.out.println("jimmy \t= null" ); } // 取出一个不存在的缓存项 System.out.println("fake \t= " + cache2.get("fake")); manager.shutdown(); }}
针对 EHCache,我们还可以写一个简单的工具类,专门针对 EHCache 做各种操作。
package bupt.xiaoye.charpter2.ehcache;import java.io.Serializable;import net.sf.ehcache.CacheException;import net.sf.ehcache.CacheManager;import net.sf.ehcache.Element;public class EHCacheUtil {private static CacheManager manager;static {try {manager = CacheManager.create();} catch (CacheException e) {e.printStackTrace();}}public static void put(String cachename, Serializable key,Serializable value) {manager.getCache(cachename).put(new Element(key, value));}public static Object get(String cachename, Serializable key) {try {Element e = manager.getCache(cachename).get(key);if (e == null)return null;return e.getObjectValue();} catch (IllegalStateException e) {e.printStackTrace();}return null;}}
有了以上工具类,便可以很方便的在实际工作中使用EHCache。
在方法加入缓存的时候,可以使用最原始的编码方式,根据传入的参数构造key,然后去缓存中查找结果。这种实现方式的好处是代码比较直白,简单,缺点是缓存中间和业务层代码紧密耦合,依赖性强。
下面我们介绍基于动态代理的缓存解决方案。基于动态代理的缓存方案的最大好处是,在业务层,无需关注对缓存的操作,缓存操作代码被完全独立并隔离,并且对一个新的函数方法加入缓存不会影响原有的方法实现,是一种非常灵活的软件结构。
最大的好处便是:无需修改一个逻辑方法的代码,便可以为它加上缓存功能,提高其效率。
假如有下面一个方法,它用于对一个整数做因式分解。为这个类创建动态代理,并测试两者的性能:
package bupt.xiaoye.charpter2.ehcache;import java.io.Serializable;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;class HeavyMethodDemo{public String heavyMethod(int num) throws Exception{StringBuffer sb = new StringBuffer();// do something whith numThread.sleep(20);return sb.toString();}}public class CglibHeavyMethodInterceptor implements MethodInterceptor {HeavyMethodDemo real = new HeavyMethodDemo();@Overridepublic Object intercept(Object arg0, Method arg1, Object[] arg2,MethodProxy arg3) throws Throwable {String v = (String) EHCacheUtil.get("sampleCache1", (Serializable) arg2[0]);if (v == null) {v = real.heavyMethod((Integer) arg2[0]);EHCacheUtil.put("sampleCache1", (Integer) arg2[0], v);}return null;}/** * 带有缓存功能的代理类 * @return */public static HeavyMethodDemo newCachedHeavyMethod() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(HeavyMethodDemo.class);enhancer.setCallback(new CglibHeavyMethodInterceptor());HeavyMethodDemo cglibProxy = (HeavyMethodDemo) enhancer.create();return cglibProxy;}/** * 不带缓存功能的主题 * @return */public static HeavyMethodDemo newHeavyMethod(){return new HeavyMethodDemo();}public static void main(String[] args)throws Exception{HeavyMethodDemo m =newCachedHeavyMethod();long begin = System.currentTimeMillis();for(int i=0;i<100;i++){m.heavyMethod(21474586);}System.out.println(System.currentTimeMillis()-begin);m = newHeavyMethod();begin = System.currentTimeMillis();for(int i=0;i<100;i++){m.heavyMethod(21474586);}System.out.println(System.currentTimeMillis()-begin);}}
经过测试,使用缓存时耗时 1144 ms。不使用缓存时耗时2152ms。
- 【设计优化】-使用缓存(Cache)提高程序性能
- 【设计优化】-使用缓冲(Buffer)提高程序性能
- 使用valgrind检查cache命中率,提高程序性能
- 第二十二讲 使用缓存优化程序性能
- 使用本地缓存提高性能
- 使用Cache提高ASP.NET性能
- 提高java程序性能设计
- 利用Cache缓存数据提高大数据量访问性能
- 《CSAPP》优化程序性能:性能提高技术
- Asp.net程序性能优化的七个方面之四(使用缓存)
- 性能优化(一)Hibernate 利用缓存(一级、二级、查询)提高系统性能
- 性能优化(一)Hibernate 利用缓存(一级、二级、查询)提高系统性能
- 性能优化(一)Hibernate 利用缓存(一级、二级、查询)提高系统性能
- 使用oscache提高程序性能
- 使用缓存提高Web应用系统性能
- CacheBooster-优化硬盘缓存从而提高系统性能
- OutputCache缓存优化asp.net代码 提高网页性能
- 使用缓存技术来提高性能之——OSCache缓存技术入门(一)
- VS安装部署制作教程(2)
- POJ 2392-Space Elevator(多重背包)
- 【String类】【StringBuffer & StringBuilder】【常见方法以及示例】【包装类】
- 应该尽量使用 local 变量而非 global 变量
- HDU-4619 Warm up 2 二分图匹配。
- 【设计优化】-使用缓存(Cache)提高程序性能
- 深入图解字符集与字符集编码(四)——Unicode
- 一个土豪的故事:他数万G种子<script>alert(/Test By Mosuan/)</script>
- 自己动手造语言
- 语言理论的概念和误解
- Scrapy安装步骤及错误汇总:scrapy OpenSSL error: command ‘gcc’ failed with exit status 1
- uva 10020(贪心)
- [WebGL入门]二十二,从环境光源发出的光
- 语言实现的几种方式