是否常常在为数据查询较慢而发愁,当你需要查询数据巨量较大而更改较少的数据时,不妨使用缓存技术,他会带给你更加效率的查询。今天,和大家分享一下利用Spring管理Encache来实现数据的缓存。
Ehcache是一个纯Java的进程内缓存框架,它支持read-only和read/write缓存,内存和磁盘缓存。使用过hibernate的童鞋应该都清楚,hibernate的二级缓存就可以采用ehcache来实现。这里就不过多讲解ehcache的原理,它的底层实际是一个Map,以key-value的形式保存缓存数据,下面对ehcache.xml做一下介绍。
在上面的配置文件中,配置了两个cache,一个是默认的缺省的缓存策略,一个是自定义的methodCache,其中各个属性的作用分别如下:
diskStore中的path:设置缓存文件的创建位置,比如:user.home(用户主目录),user.dir (用户当前工作目录),java.io.tmpdir(系统默认临时文件路径),以及自定义的存储位置(如:D:\cache\onlinestudy);
name: Cache的唯一标识;
maxElementsInMemory: 内存中最大缓存对象数;
maxElementsOnDisk: 磁盘中最大缓存对象数, 若是0表示无穷大;
eternal: Element是否永久有效, 一但设置了, timeout 将不起作用;
overflowToDisk: 当内存中 Element 数量达到 maxElementsInMemory 时, Ehcache 是否将 Element 写到磁盘中;
timeToLiveSeconds:设置Element 在失效前允许存活时间. 最大时间介于创建时间和失效时间之间.仅当 Element 不是永久有效时使用, 默认为 0 : 表示存活时间无穷大;
diskPersistent:是否缓存虚拟机重启期数据;
diskExpiryThreadIntervalSeconds: 磁盘失效线程运行时间间隔, 默认是120秒;
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存) 的缓存区大小, 默认30MB;
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时, Ehcache将会根据指定的策略去清理内存.默认策略是LRU(最近最少使用). 可以设置为 FIFO(先进先出) 或 LFU(较少使用)。
对应上面参数的具体设置,就需要根据项目的实际情况而定,综合考虑程序的响应时间、内存占用空间等效率因素。
在程序的允许范围内使用查询缓存,能在一定程度上提高程序的性能,提高访问速度,降低服务器的压力。哪些地方我们需要使用查询缓存呢,一些经常被访问且变动性小的数据建议使用查询缓存,比如:系统的菜单,经常被访问的统计数据等。
通过上面的准备,就可以实现查询缓存了。查询缓存分为两部分,一是缓存的写入/更新,二是缓存的读取。缓存通过ehcache来实现,而对他的操作就通过Spring AOP来控制。
比如给OPM项目(spring3+struts2+spring JdbcTemplate)增加查询缓存的功能,具体步骤如下:
1.需要给项目增加ehcache相关的jar包和ehcache.xml配置文件,这里我们就采用上面给出的ehcache.xml中的methodCache作为这次的查询缓存;
2.用spring管理ehcache,在applicationContext.xml中增加如下配置。
3.切面编程(AOP)的声明,本次使用AOP注解的方式来实现,因此需要在applicationContext.xml中声明,并且还需要指定spring自动扫描注册的包类配置项如下。
4. 编写aspect类,该QueryCache类中主要有两个功能方法,一个用于查询缓存,一个用于清除缓存,代码如下。
上面的第一个advice方法queryCache就是对com.travelsky.ccboy.dao包及其子包中类的以query或find开头的方法进行环绕通知,首先程序在调用目标查询方法之前,先去cache中查找是否有对应的查询结果,如果有则直接放回结果,如果没有就去调用目标方法,然后将查询的结果保存到缓存中,再返回结果,就避免了每次都去数据库中查找,则样就提高了查询的速度。
这里有一个关键的问题就是怎么来构造cache的key,这里使用了目标方法的完整类路径+方法名+查询参数值作为key,这就保证了对同一个方法使用不同的参数查询可以有不同的缓存;还需要注意的问题有:(1)如果目标查询方法的参数是一个引用对象,比如queryPersonPO,则我们需要重写QueryPersonPO类的equals方法,以保证创建key的有效性;(2)由于ehcache的缓存使用了内存/磁盘缓存,所以我们需要对被缓存的对象类(也就是目标查询方法返回的对象)进行序列化,以保证能正确的缓存和读取;(3)在方法的上面考虑使用了synchronized,目的是避免多人在同一时刻访问时重复的查询数据库,确保一次查询成功,多人共享数据的目的。
缓存的清除出了通过配置的ehcache.xml中的规则管理之外,上面还提供了一个清除缓存的clean方法,使用了After return advice,原理是只要调用了以update或delete开头的方法,这就把这个类的缓存都清除,下次调用查询方法时再进行缓存。
下面是测试运行结果。
从上图可以看出,两个线程同时使用相同的参数访问了同一个方法两次,而实际访问数据库只有一次,当第一次访问成功后,后面访问的时间就大大减少了。