Ehcache支持分层缓存的概念,这节主要介绍不一样的配置选项,同时也解释了规则和最佳实践。java
一. 数据缓存到堆外缓存
当在cache中除了有heap层以外,有一些须要注意的:数据结构
基于上述两点,你须要认识到数据应该使用二进制表示,而且考虑何如进行序列化和反序列化,由于他在cache性能中是重要的指标。你能够参考Seializers章节来了解序列化和反序列化。并发
这也说明了某些配置在纸面上说有意义,可是在实际使用中可能不能提供最佳性能。less
二. 单层设置jvm
全部的层级配置均可以是单独使用的,例如你能够单独使用offheap或者clustered的cache。性能
下面列出一些单层配置:测试
简单的定义一个单层配置:ui
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, (1) ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(2, MemoryUnit.GB)).build();(2)
(1). 在configuration builder中首先配置key和value的类型。this
(2). 指定想使用哪一个存储层,这里咱们只使用offheap层。
1. Heap层
The starting point of every cache and also the faster since no serialization is necessary. You can optionally use copiers (see the section Serializers and Copiers) to pass keys and values by-value, the default being by-reference.
Heap层的大小能够按照entries或者字节来分配。
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES); (1)
//or ResourcePoolsBuilder.heap(10); (2)
//or
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB); (3)
(1). heap中只容许存储10个entries,当超出时会删除某些entry。
(2). 一个简版的指定10个entries。
(3). 指定heap大小为10MB,超出时会删除某些entry。
按照字节来分配heap大小存在的问题:
除了heap层外,其余层计算cache的大小是很容易的。你能够经过计算序列化后entries的大小来增长或减小缓存的大小。
可是当heap层使用字节大小来代替entries个数来设置heap size时,就有点复杂了。
缓存数据的大小和数据结构影响了运行时性能。
CacheConfiguration<Long, String> usesConfiguredInCacheConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, MemoryUnit.KB) (1) .offheap(10, MemoryUnit.MB)) (2) .withSizeOfMaxObjectGraph(1000) .withSizeOfMaxObjectSize(1000, MemoryUnit.B) (3) .build(); CacheConfiguration<Long, String> usesDefaultSizeOfEngineConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, MemoryUnit.KB)) .build(); CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withDefaultSizeOfMaxObjectSize(500, MemoryUnit.B) .withDefaultSizeOfMaxObjectGraph(2000) (4) .withCache("usesConfiguredInCache", usesConfiguredInCacheConfig) .withCache("usesDefaultSizeOfEngine", usesDefaultSizeOfEngineConfig) .build(true);
(1). 这将会限制heap层使用的内存的大小,而且又会有一个计算对象大小的成本。
(2). 设置offheap大小为10MB。
(3). 能够经过额外两个参数更进一步的设置大小,第一个参数是指定了当遍历对象图时最大的对象数量(默认是1000),第二个参数定义了单个对象大小的最大值(默认是Long.MAX_VALUE),若是超过任何一个配置的大小,那么entry将不能被存进cache。
(4). 提供了默认的配置给CacheManager,若是有显示定义的,那么将覆盖这两个值。
2. Off-heap层
若是你想使用off-heap,你须要定义Resuource Pool而且指定你想要分配的空间大小。
ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB);(1)
(1). 只分配了10MB给off-heap,当超出时会删除某些entry。
上面的例子只分配很小的off-heap空间,你能够分配更多的空间来使用。
记住数据存储在off-heap,须要进行序列化和反序列化,因此他的速度会比heap层慢。
你能够把在heap中影响GC性能的数据,存储在off-heap。
不要忘了设置java 运行时参数 -XX:MaxDirectMemorySize选项,他的大小根据off-heap的大小来设置。
3. Disk层
对于Disk层,数据存储在磁盘上,这个磁盘越快那么访问的数据也就越快
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() (1) .with(CacheManagerBuilder.persistence(new File(getStoragePath(), "myData"))) (2) .withCache("persistent-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().disk(10, MemoryUnit.MB, true)) (3) ) .build(true); persistentCacheManager.close();
(1). 得到一个PersistentCacheManager,它比普通的CacheManager多了destory caches。
(2). 提供数据存储的位置
(3). 给cache中使用的Disk,定义一个resource pool,第三个参数是一个boolean类型的,它用来决定是否将Disk pool持久化。若是设置成true,那么就持久化存储。当使用两个参数的版本时disk(long,MemoryUnit)这个pool不会被持久化。
上面的例子分配了很小的内存空间,你能够分配更多的空间来使用。
持久化意味着,当JVM重启时,cache中全部的东西都会保存下来,而且启动后会在相同的位置建立持久化的CacheManager。
注意:Disk层不能在CahceManager之间共享,在同一时间一个CacheManager只能有一个持久化目录。
记住数据存储在磁盘,必需要序列化/反序列化而且要读/写到磁盘中,因此他的速度要比heap和off-heap慢不少。因此在如下状况使用磁盘存储:
注意:Ehcache 3只提供了在正常调用关闭时(close())才会持久化数据,若是jvm崩溃那么数据就不能完整性,在重启后,Ehcache会检测到CacheManager没有正常的关闭而且会在使用前清除磁盘存储的数据。
磁盘存储被分红多段,提供并发访问的能力,因此持有以打开文件的指针,默认是16。有时你可能须要减少并发量,节省资源你就须要减少段的数量。
String storagePath = getStoragePath(); PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence(new File(storagePath, "myData"))) .withCache("less-segments", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().disk(10, MemoryUnit.MB)) .add(new OffHeapDiskStoreConfiguration(2)) (1) ) .build(true); persistentCacheManager.close();
(1). 定义一个OffHeapDiskStoreConfiguration实例指定段的数量。
4. 集群存储
集群层意味着客户端链接到Terracotta服务组,那里存储着cahe,这也能够做为jvms之间的共享cache。
二. 多层配置
若是你想使用多层存储,那么你必须遵照一些约定:
Ehcache要求,heap大小 < off-heap大小 < disk大小,虽然Ehcache没法验证 基于count-entry配置的heap 和 基于字节配置的off-heap和cluster大小关系,可是在测试时用户要保证这点。
根据上面的说明,以下配置是有效的:
以下是使用heap,offheap和clustered的例子
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(cluster(CLUSTER_URI).autoCreate()) (1) .withCache("threeTierCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) (2) .offheap(1, MemoryUnit.MB) (3) .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB)) (4) ) ).build(true);
(1). 指定集群信息,告诉怎么去链接Terracotta服务集群。
(2). 定义一个heap层,这是最小最快的存储层。
(3). Define the offheap tier. Next in line as cacheing tier.
(4). Define the Clustered tier. The authoritative tier for this cache.
三. Resource pools
层的设置是经过使用resource pools,大多数使用ResourcePoolsBuilder。来看一下以前的一个例子
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence(new File(getStoragePath(), "myData"))) .withCache("threeTieredCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .offheap(1, MemoryUnit.MB) .disk(20, MemoryUnit.MB, true) ) ).build(true);
这个cache使用了3层(heap,offheap,disk)。这些层被建立并链接起来都是经过使用ResourcePoolsBuilder。层的声明顺序是没有关系的,由于每一个层都有一个height,层的高度越高,客户就会越会先使用该层。
resource pool只是一个配置信息,它不是一个真正的pool,不能够在多个cache之间共享,对于理解这点很是重要。考虑下面的这段代码:
ResourcePools pool = ResourcePoolsBuilder.heap(10).build(); CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("test-cache1", CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, pool)) .withCache("test-cache2", CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, pool)) .build(true);
最终你会获得两个cache,每一个都包含了10个entries。而不是这两个cache共享10个entries。pool历来都不会在cache之间共享,除了clustered cache,它是能够被共享或独立使用的。
更新ResourcePools
能够给正在运行中的cache,调整大小。
注意:updateResourcePools()只容许你调整heap的大小,而不准调整pool的类型。因此你不能改变off-heap和disk层的大小。
ResourcePools pools = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(20L, EntryUnit.ENTRIES).build(); (1) cache.getRuntimeConfiguration().updateResourcePools(pools); (2) assertThat(cache.getRuntimeConfiguration().getResourcePools() .getPoolForResource(ResourceType.Core.HEAP).getSize(), is(20L));
(1). 你须要建立一个新的ResourcePools对象,并使用ResourcePoolsBuilder设置heap的大小,而后将该对象传给上述方法来触发更新机制。
(2). 为了更新ResourcePools的容量,可使用RuntimeConfiguration中的updateResourcePools(ResourcePools)方法,而后将ResourcePools对象传进去触发更新操做。
四. Destory Persistent Tiers
Disk层和Clustered层是两个持久层,那就意味着当JVM中止时全部的数据仍然会保存在磁盘或集群中。
可是你可能想将他们全都删除掉,你可使用PersistentCacheManager类,他有以下方法:
destory()
这个方法销毁全部与cache manager相关的数据(包括cache)。必定关闭这个CacheManager或者这个CacheManager没有初始化才能够调用该方法。一样,对于clustered层,不能有其余的CacheManager链接到当前要被关闭的CacheManager上。
destoryCache(String cacheName)
这个方法销毁了给定的cache,一样这个被销毁的cache不能正在被其余的CacheManager使用。
五. Sequence Flow for Cache Operations with Multiple Tiers(多层cache的操做顺序流)
为了理解在使用多层存储时,不一样的缓存层发生了什么。下面有个Put和Get操做的例子。下面的序列图虽然简单可是仍然显示最重要的部分。
你须要注意一下几点:
注意:若是你的authoritative层越慢,那么你的put操做就会越慢。对于正常cache的要使用,这是没问题的。由于get操做的频率要大于put操做的频率。可是若是相反put频率大于get频率那可能意味着一开始你就不该该考虑使用cache。