目录
1 堆内存(MemoryStore)
1.1 指定可用内存
1.2 驱除策略
1.3 元素过期
2 非堆内存(BigMemory)
3 磁盘(DiskStore)
3.1 指定可用容量
3.2 元素过期
在Ehcache中对于缓存的存储主要有三种方式:分别是堆内存、非堆内存和磁盘。其中非堆内存是针对于企业版Ehcache才有的功能,它可以不受Java GC的影响,能够创建很大的缓存。
我们通常所有的MemoryStore实际上就是堆内存存储。MemoryStore总是可用的,所有的元素都可以储存在MemoryStore中。MemoryStore是线程安全的,相比另外两种储存方式而言其访问速度也是最快的。通常我们在往缓存里面添加元素的时候,其首先就是存放在MemoryStore里面的,但是我们又不能说连续不断的往MemoryStore里面存放元素,这就涉及到到底能放多少元素的问题。
Ehcache规定我们在使用一个Cache时必须在CacheManager级别指定可用的内存大小或者是在Cache级别指定可用的内存大小或所允许存放的元素的最大数量。在CacheManager级别指定的内存大小是其内部所有Cache一起所能使用的内存的最大量。CacheManager级别指定内存大小是通过maxBytesLocalHeap来指定的,如:
上面指定了我们的CacheManager所能使用的最大内存是500M。CacheManager级别指定了内存大小后我们在Cache上也可以指定其能使用的最大内存,但不能指定其所能存储元素的最大数量。另外,如果我们的CacheManager没有指定可用的内存大小,我们可以通过maxBytesLocalHeap在Cache级别指定可用的内存大小,或者通过maxEntriesLocalHeap在Cache级别指定允许储存元素的最大数量,但是maxEntriesLocalHeap和maxBytesLocalHeap不能同时使用。如下在CacheManager级别使用maxBytesLocalHeap,然而在Cache级别使用maxEntriesLocalHeap是不行的。
如下在Cache级别同时使用maxBytesLocalHeap和maxEntriesLocalHeap也是不行的。
那么当我们内存中的元素大小或者数量超过了预设的大小之后怎么办呢?这个时候如果我们设置了允许溢出,如overflowToDisk或overflowToOffHeap,则会把某些元素溢出到对应的储存器中。如果不允许溢出,则会先删除原有的某些元素。至于是哪个元素溢出、哪个元素删除则在不存在超时的情况下与我们的内存驱除策略有关,这是通过Cache的memoryStoreEvictionPolicy属性来指定的。其可选值有LRU、LFU和FIFO,默认是LRU。但是如果有过期元素存在的话则会优先驱除已经过期的元素,然后再考虑驱除策略memoryStoreEvictionPolicy。
LRU(Least Recently Used):最近最少使用。当我们把一个元素储存到Cache中或者从Cache中取出时都会更新该元素的最后使用时间。当采用最近最少使用原则进行驱除时会优先把最后使用时间最早的元素进行驱除。
LFU(Least Frequently Used):最不常使用的。每次我们从Cache中获取一个元素时都会更新该元素的hitCount属性值加1。当采用最不常使用原则进行驱除时hitCount属性值最小的元素将优先驱除。
FIFO(First In First Out):先进先出。当采用这种驱除原则时将优先驱除最先储存的元素。
在上文中我们已经知道在不存在元素过期的情况下,驱除哪个元素是与我们的内存驱除策略有关的。但如果存在过期元素的话则会优先驱除已经过期的元素。关于控制元素是否过期我们可以通过cache的几个属性来定义。
timeToIdleSeconds:单位是秒,表示一个元素在不被请求的情况下允许在缓存中存在的最长时间。默认值是0,表示不限制。
timeToLiveSeconds:单位是秒,表示一个元素不管有没有被使用,其在缓存中允许存在的最长时间。默认是0,表示不限制。一般timeToLiveSeconds要比timeToIdleSeconds长,否则就timeToIdleSeconds就失去意义了。
eternal:boolean类型,表示是否永恒,默认为false。当设为true时,表示缓存中的元素永远不会过期,timeToIdleSeconds和timeToLiveSeconds就失去作用了。这个时候元素就只能由驱除策略来进行驱除了。
当我们的元素过期以后,为了保持Cache的性能,Ehcache不一定马上就会将过期的元素删除或者驱除到其它存储容器中,它可能还在原来的位置。之所以说不一定是因为有可能当一个元素过期时恰好Cache需要删除元素或者驱除元素到其它存储容器中,这个时候我们的过期元素将优先被删除或者驱除。另外,当我们在请求一个元素的时候,如果Ehcache发现该元素已经过期的话也会立刻将该元素删除。
在上面的配置中,我们指定了该Cache允许元素不被请求的时间是10分钟,最大存活时间是1小时,使用的驱除策略是LFU(最不常使用)。
非堆内存存储是针对于企业版才有的功能,它可以不受Java GC的影响,能够创建很大的缓存。BigMemory保存的都是一个个的字节,在保存元素的时候Ehcache会对元素进行序列化再保存到BigMemory中,然后在读取的时候又会把读取到的字节进行反序列化。所以存放在其中的元素的key和value都必须是能够序列化的。
磁盘存储可以存储内存中驱除过来的元素,也可以在系统重启的时候将内存中的缓存信息保存起来,供系统重新启动后使用。磁盘存储是非必须的,但是使用DiskStore的时候我们需要指定一个磁盘目录来存放缓存信息。这可以在ehcache.xml文件中的ehcahce元素下的定义一个diskStore元素并指定其path属性。由diskStore元素是定义在ehcache元素下我们看出diskStore在CacheManager范围内是共享的,其是线程安全的。如果我们没有定义diskStore元素时,DiskStore会使用默认的目录作为其存储目录,该目录就是java.io.tmpdir,即Java的临时目录。当然我们也可以指定一个绝对路径。当我们指定diskStore元素的path为以下值时会被替换为实际对应的目录:
l user.home:用户的家目录。
l user.dir:用户的当前工作目录。
l java.io.tmpdir:Java临时目录。
l 在命令行指定的属性,如“java -Dehcache.disk.store.dir=D:\\abc …..”。
子目录的话可以这样指定:user.home/ehcache。
此外需要注意的是因为DiskStore是把信息存放在磁盘上的,所以我们存放在磁盘上的元素必须是可以序列化的。CacheManager的DiskStore路径一旦设置好了之后将不能再更改。如果硬是更改了,那么我们的CacheManager需要基于新的路径重新建立。
指定可用容量的时候我们可以在CacheManager级别通过maxBytesLocalDisk来指定。而在Cache级别我们可以通过maxBytesLocalDisk和maxEntriesLocalDisk来指定。因为DiskStore是可选的,所以这些属性也都是可选的。另外不像MemoryStore那样,我们在Cache级别上可以同时指定maxBytesLocalDisk和maxEntriesLocalDisk。如:
DiskStore中驱除元素跟MemoryStore中驱除元素的规则是不一样的。当往DiskStore中添加元素且此时DiskStore中的容量已经超出限制时将采用LFU(最不常用)驱除规则将对应的元素进行删除,而且该驱除规则是不可配置的。
DiskStore中的元素过期跟MemoryStore中的元素过期是同样定义的。对于每一个Cache而言都拥有一个线程用于检查并移除其中的过期元素。至于多久检查一次,我们可以通过cache元素的diskExpiryThreadIntervalSeconds 属性来指定,默认是每两分钟检查一次。