基于ehcache2.10.5 官方文档: http://www.ehcache.org/generated/2.10.4/html/ehc-all/
ehcache简介
ehcache是基于标准java缓存(JSR-107)的开源缓存,纯用java语言编写的本地缓存框架,具有使用简单、访问快速、代码小巧等特点,专门服务于java应用程序。ehcache出生早、使用广泛,为很多开源框架和应用所使用如Hibernate、mybatis、spring等。
ehcache对比redis
ehcache有好多功能是不如redis的,比如开源的ehcache不支持数据持久化到硬盘和数据的恢复,而且单就缓存服务本身的处理速度上,ehcache也是不如redis的,毕竟一个是用C语言写的,一个是用java写的。但ehcache好就好在足够简单,上手较快,仅几分钟就能使用上缓存,而且ehcache是处于进程内的本地缓存,在数据传输的速度上是要比redis快的。redis是基于socket的网络传输,服务器与redis传递数据大部分时间都花费在网络传输上,要不人家redis总说,俺的性能瓶颈是网络呢!
1、ehcache具有以下特性:
1、数据可以设置过期时间
缓存为了加快读写,数据一般都是放在内存中的,就目前来看,内存条还有没有像硬盘一样便宜和大容量,所以为了节省内存的资源占用,必须要给内存中的数据设置过期时间,使之过期之后自动淘汰掉。ehcache支持给数据设置过期时间,不但可以给cache设置过期时间,基于编程的方式时还能给element设置过期时间。
2、数据支持淘汰策略
内存的容量毕竟是有限的,当内存中的数据量达到一定量时,就要设计一种算法淘汰掉一些旧的数据,好为新的数据腾出地方来。ehcache目前支持三种数据淘汰算法:LRU、LFU和FIFO。
LRU(Least Recently Used,最近最少使用):LRU是默认设置。当将元素放入cache或使用get调用从cache中检索元素时,将更新元素最后使用的时间戳(LastAccessTime),添加时会把最后使用时间最早(LastAccessTime最小)的元素进行驱除。
LFU(Least Frequently Used,频率最少使用):每个元素被get调用时都会更新元素的命中次数(hitCount)。当添加一个新元素时,若达到内存存储的最大限制时,将删除命中次数(hitCount)最小的元素,即“不常用”元素。
FIFO(First In First Out,先进先出):队列的性质。当添加一个新元素时,若达到内存存储的最大限制时,最先放进该cache的元素会最先被删除。
3、提供了条件查询api
ehcache提供了一些查询api,基本可以像查询RDB一样,查询ehcache中的数据,但是速度却比RDB要快的多。
4、支持后写模式
解决了数据库被频繁写入的性能瓶颈,大大提高了数据库写数据的能力。
5、磁盘临时存储
开源版本中,当堆内存的容量不够时,ehcache会将数据临时存储到磁盘中,只在内存中保存一个磁盘的索引。所以理论上ehcache的容量上限跟磁盘容量有直接关系。
2、ehcache的数据存储结构
清楚ehcache的存储结构是十分必要的,对接下来使用ehcache将会带来很大的帮助。
ehcache可以创建多个cacheManager,每个cacheManager包含多个cache,每个cache包含多个element。
1、cacheManager:缓存管理器,用来管理和操作cache。可以基于xml文件创建也可以代码中创建,一个ehcache.xml文件代表着一个cacheManager的配置,所以可以创建多个ehcache-xxx.xml,根据这些不同的ehcache-xxx.xml配置文件,创建不同的cacheManager实例。
2、cache:缓存,用来管理和操作里面的element,还可以对里面的element统一设置过期时间和淘汰策略等属性。
3、element:元素,ehcache中最小的结构单元,结构为key-value对,key可以是任意类型,value也可以是任意类型,此外还可以单独对element进行设置过期时间、命中次数、最后一次访问时间戳等操作。
简单来说,一个cacheManager相当于一个Map<Map<String,Object>>结构,一个cache就是一个hashMap,element相当于map里面的一个个的key-value对。
3、ehcache的数据存储位置
ehcache提供了三种存储数据的方式来优先级依次递减:
1、jvm堆内存(默认开启,会被javaGC,总大小取决于java堆内存大小)
当添加的元素超过设置的堆内存最大内存大小时,如果未启用溢出,则将删除现有元素,不会开启数据过期策略。若启用了溢出,则依次存放到下一级缓存位置中,并将检查所有数据是否过期,如果过期,则将其删除;
调整大小:设置内存大小的属性使用Java -Xmx语法(例如:“ 500k”,“ 200m”,“ 2g”)采用整数,最小单位(字节)
maxBytesLocalHeap:设置ehcache占用堆内存的字节数。不能超过jvm堆内存的大小
maxEntriesLocalHeap:设置ehcache在jvm堆内存中能存储entry的最大数量,不能超过Integer.MAX_VALUE的值。(一个entry就是一个element,也就是一个键值对,类似hashMap中的entry)
2、堆外内存(开源版本不支持,我们使用的就是开源版本)
需要使用ehcache的相关产品才支持堆外内存。另外ehcache不支持数据持久化到硬盘和jvm重启后数据恢复到内存,也是需要使用ehcache的相关产品才行,都是钱啊。
调整大小:
maxBytesLocalOffHeap
3、磁盘(默认开启)
值得一提的是,ehcache会在JVM启动时自动从类路径下找ehcache.xml当做配置文件,如果找不到再找ehcache-failsafe.xml这个文件,而ehcache在依赖的根目录下就为我们提供了ehcache-failsafe.xml文件,所以当我们没有在项目中配置ehcache的配置文件时,就默认使用ehcache为我们提供的ehcache-failsafe.xml配置文件,ehcache-failsafe.xml文件默认开启了磁盘临时缓存,存储的位置就是java.io.tmpdir的值。
开启:
使用<persistence strategy="localTempSwap">配合<diskStore path="java.io.tmpdir"/>来开启和设置磁盘缓存目录。使用localTempSwap策略时,可以在Cache或CacheManager级别使用maxEntriesLocalDisk或maxBytesLocalDisk来控制磁盘层的大小。若超过磁盘存储大小限制,ehcache将使用LFU算法来清除磁盘数据,同时删除内存中的数据索引。
关闭磁盘缓存:
若想关闭磁盘存储,注释掉配置文件中的<diskStore/>标签元素即可。
调整大小:
maxBytesLocalDisk:设置ehcache磁盘缓存的最大字节数。
maxEntriesLocalDisk:设置ehcache磁盘缓存中entry的最大数量,不能超过Integer.MAX_VALUE的值
总的来说,
在cacheManager级别只能使用maxBytesLocalHeap和maxBytesLocalDisk来设置总的占用堆内存和磁盘的大小上限,不设置则默认上限为jvm堆内存设置大小和磁盘容量。
在cache级别可以使用这四个属性,来设置当前cache占用的堆内存和磁盘大小,当然多个cache的总大小不能超过cacheManager配置的上限。
maxBytesxxx和maxEntriesxxx不能单独在cacheManager和cache中同时出现,例如cacheManager中配置了maxBytesLocalHeap,cache中要同时配置maxBytesLocalHeap和maxEntriesLocalHeap才行,不然会报属性不兼容的异常。若不知道怎么配,cacheManager和cache中都配置maxBytesLocalxxx就行了,不会出错。
2、HelloWorld
1、引入ehcache的依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.5</version>
</dependency>
2、在类路径下放一个文件ehcache.xml,内容大致如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
name="cacheManager1"
maxBytesLocalHeap="200M"
maxBytesLocalDisk="20G"
>
<!-- 当内存中的数据达到设置上限后,磁盘临时缓存位置 -->
<diskStore path="D:/ehcacheTempData"/>
<!-- 配置默认的cache。当代码中使用cacheManager.addCache(cacheName);方法时,若该cacheName对应的cache不存在,将默认使用该配置创建该cache。
注意,基于spring缓存注解时,必须要有cache的name与cacheNames属性对应才行,否则会报Cannot find cache name xxx异常-->
<defaultCache
maxBytesLocalHeap="100M"
maxBytesLocalDisk="10G"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<!--启用临时磁盘存储内存溢出的数据,数据存储在diskStore标签指定的地方-->
<persistence strategy="localTempSwap"/>
</defaultCache>
<!-- 单独配置一个cache
name:缓存名称。
maxBytesLocalHeap:占用堆内存的大小,使用Java -Xmx语法(例如:“ 500k”,“ 200m”,“ 2g”)采用整数。
maxBytesLocalDisk:占用磁盘的大小,使用Java -Xmx语法(例如:“ 500k”,“ 200m”,“ 2g”)采用整数。
maxEntriesLocalHeap:堆内存中的最大entry(ehcache中的元素)数量。
maxEntriesLocalDisk:磁盘中的最大entry(ehcache中的元素)数量。
eternal:对象是否永久有效,一但设置了,设置的过期时间将失效,但会被淘汰。
timeToIdleSeconds:设置element在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,表示永久有效。
timeToLiveSeconds:设置element的存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0,表示永久有效。
//overflowToDisk:当内存中element数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中(已过期)。
//diskPersistent:是否缓存虚拟机重启期数据(已过期,开源版本无效)。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
-->
<cache name="empCache"
eternal="true"
timeToIdleSeconds:3600
timeToLiveSeconds:3600
maxBytesLocalHeap="50M"
maxBytesLocalDisk="5G"
/>
</ehcache>
3、代码方式创建和配置CacheManager、Cache和element,以及element的增删改查
/**
* ehcache缓存的存储结构和元素的增删改查
* @throws InterruptedException
*/
@Test
public void test01() throws InterruptedException {
/**
* 创建cacheManager可以调用CacheManager的静态方法create()或newInstance(),
* 这俩方法会根据cacheManager的name,检查是否已经创建了同名的cacheManager,若有则返回已有的,否则创建新的cacheManager。
*/
//使用默认配置文件创建单例的CacheManager,先到类路径下的找ehcache.xml,若没有再找ehcache-failsafe.xml(ehcache的依赖中默认就提供了这个配置文件)
CacheManager singleTonCacheManager = CacheManager.create();
//使用指定的配置文件创建新的CacheManager
CacheManager cacheManager = CacheManager.newInstance(getClass().getClassLoader().getResourceAsStream("ehcache_config.xml"));
//创建并配置cache,和在ehcache.xml中配置一样
CacheConfiguration cacheConfiguration = new CacheConfiguration("测试cache1",1000);
//统一设置cache中元素过期时间
cacheConfiguration.setTimeToLiveSeconds(60);
cacheConfiguration.setTimeToIdleSeconds(60);
/*cacheConfiguration.setMaxEntriesLocalHeap();
cacheConfiguration.setMaxEntriesLocalDisk();
cacheConfiguration.setMaxBytesLocalHeap();
cacheConfiguration.setMaxBytesLocalDisk();*/
Cache cache = new Cache(cacheConfiguration);
//先要将创建的cache放到cacheManager中
cacheManager.addCache(cache);
//创建一个element元素
Element element = new Element("key1","value1");
Element element2 = new Element("key2","value2");
//1、将该元素放到cache中
cache.put(element);
cache.put(element2);
//获取元素的过期时间
int timeToLive = element.getTimeToLive();
//单独设置元素的过期时间
element.setTimeToLive(100);
long creationTime = element.getCreationTime();
long expirationTime = element.getExpirationTime();
long hitCount = element.getHitCount();
long version = element.getVersion();
//timeToLive:60,creationTime:1604556728911,expirationTime:1604556788911,hitCount:0,version:1
System.out.println("timeToLive:"+timeToLive+",creationTime:"+creationTime+",expirationTime:"+expirationTime+",hitCount:"+hitCount+",version:"+version);
//获取cache
Cache cache1 = cacheManager.getCache("测试cache1");
Element elem1 = cache1.get("key1");
Element elem2 = cache1.get("key2");
//获取元素的key和value
Object key1 = elem1.getObjectKey();
Object value1 = elem1.getObjectValue();
//[ name = 测试cache1 status = STATUS_ALIVE eternal = false overflowToDisk = true maxEntriesLocalHeap = 1000 maxEntriesLocalDisk = 0 memoryStoreEvictionPolicy = LRU timeToLiveSeconds = 60 timeToIdleSeconds = 60 persistence = none diskExpiryThreadIntervalSeconds = 120 cacheEventListeners: ; orderedCacheEventListeners: maxBytesLocalHeap = 0 overflowToOffHeap = false maxBytesLocalOffHeap = 0 maxBytesLocalDisk = 0 pinned = false ]
System.out.println(cache1);
//2、获取元素[ key = key1, value=value1, version=1, hitCount=1, CreationTime = 1604555900407, LastAccessTime = 1604555900407 ]
System.out.println(elem1);
//[ key = key2, value=value2, version=1, hitCount=1, CreationTime = 1604557473124, LastAccessTime = 1604557473124 ]
System.out.println(elem2);
//key1:key1,value1:value1
System.out.println("key1:"+key1+",value1:"+value1);
//3、修改元素的值。同hashMap一样,相同key的后者会覆盖前者
Element element3 = new Element("key2", "value3");
cache.put(element3);
//[ key = key2, value=value3, version=1, hitCount=0, CreationTime = 1604557293391, LastAccessTime = 0 ]
System.out.println(cache1.get("key2"));
//4、删除元素element2
boolean remove = cache.remove("key2");
//删除成功返回true
System.out.println(remove);
}
好了ehcache的简介与helloWorld到这里就介绍完了。ehcache也算是一个标准的缓存框架,由于是纯java编写,所以java程序员有兴趣的话好好研究一下它的源码,无论是对代码设计还是缓存的设计都是很有帮助的。另外,有没有发现,ehcache这个单词正过来和反过来都是一样的!