ehcache3 支持分层存储的概念,本文主要记录不同选项的配置和最佳时间。
当在一个cache中,除了堆这一层外还有其它分层是,会有以下是事情发生。
- 在往jvm堆缓存区域外的层写入缓存项时,会有key和value 序列化发生。
- 在从jvm堆外缓存区读取缓存项时,会有key、value反序列化发生。
通过以上两点,我们可以知道数据的二进制形式与序列化的转换方式将对缓存性能有显著的影响。所以需要选择合适的序列化方式。
单一分层设置
所有的分层选项都可以单独使用,如可以只是用堆外内存或集群方式缓存数据。
可用配置如下
- heap 堆内缓存
- 堆外内存 offheap
- 磁盘
- 集群方式
单个资源池配置示例
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // 指定key value 类型
ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(2, MemoryUnit.GB)).build(); // 指定堆外缓存大小为2GB
堆内缓存层
由于不需要序列化,因此堆内缓存速度更快。也可以使用副本的方式来传递key和value。默认通过引用的方式。
堆内缓存配置如下
// 配置10个entries,超出将出发清除。
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES);
// or 简短配置 10个 entries
ResourcePoolsBuilder.heap(10);
// or 指定内存大小配置, 10MB
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB);
通过字节大小配置的堆
除了堆这层的缓存,其它缓存层计算缓存大小比较容易。当堆缓存使用基于大小的限制时,计算大小比较复杂。
-- 堆内缓存保存的是被缓存对象的引用,计算大小需要遍历对象引用树来计算大小。
CacheConfiguration<Long, String> usesConfiguredInCacheConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.KB) //a
.offheap(10, MemoryUnit.MB))
.withSizeOfMaxObjectGraph(1000)
.withSizeOfMaxObjectSize(1000, MemoryUnit.B)//c
.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) //d
.withCache("usesConfiguredInCache", usesConfiguredInCacheConfig)
.withCache("usesDefaultSizeOfEngine", usesDefaultSizeOfEngineConfig)
.build(true);
- a: 会限制堆内缓存的大小为10KB,会有计算对象大小的成本。
- c: 内存大小还可以进一步通过另两个参数约束,第一个参数是用来限制最大的遍历对象引用树时最大的对象数(默认1000),第二个定义单个对象的最大尺寸。当尺寸大于任何一个时,缓存条目不会被缓存。
- d:给CacheManager 提供默认的配置。
堆外内存层-直接内存
ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB);
- 堆外缓存需要序列化和反序列化,会比堆内慢。
- 对于大数据量的缓存,应该使用堆外缓存,因为GC会对缓存有严重的影响。
- 需要结合 -XX:MaxDirectMemorySize,来确保合适的堆外内存设置。
磁盘存储层
磁盘层,数据保存在磁盘上,因此磁盘越快、越专用,访问缓存数据越快。
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();
- 获取PersistentCacheManager ,相比于其它的CacheManger,其有destroy chches的功能。
- 提供数据存储位置。
- 定义用户用户缓存的磁盘资源池,第三个参数用于设定磁盘池是否持久的,若设置为true,则持久化。
持久化设置意味着缓存能够在jvm重启时保留,每次重启时,缓存信息仍然保留,并且使用以前的存储位置来创建一个新的CacheManager。
磁盘层不能在CacheManager 之间共享,一个持久化目录每次只能归属于一个 cache manager。
磁盘层同样需要序列化和反序列化,因此其比堆内和堆外缓存都慢,其使用场景应该如下:
- 缓存数据量大,不能被堆外缓存层满足。
- 磁盘缓存数据访问比从原存储获取数据要快。
- 对持久化有需求
段
磁盘缓存被分成缓存段,用于提供同步访问。默认为16个segement。可以通过减少segement的个数来减少同步和结束资源。
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))
.withService(new OffHeapDiskStoreConfiguration(2))
)
.build(true);
persistentCacheManager.close();
集群存储层
。。。
多级缓存设置
若想使用多级缓存,需要遵守以下约束
- 必须存在堆内存储层。
- 不能联合使用磁盘存储和集群存储层。
- 缓存层级越高,内存越小,即 heap < off-heap
缓存层级的有效组合如下: - heap + offheap
- heap + offheap + disk
- heap + offheap + clustered
- heap + disk
- heap + clustered
使用堆内、堆外、集群的示例
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(cluster(CLUSTER_URI).autoCreate(c -> c))
.withCache("threeTierCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))
)
).build(true);
资源池
缓存层通过 资源池来配置,简单的方式是ResourcePoolsBuilder。
ResourcePools 资源池一个配置,不实际对于一个缓存资源,不是一个在多个缓存之间共享的内存池。当使用资源池创建缓存时,会根据缓存池创建对应规格的缓存。
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);
如上, 两个缓存每一个都包含一个10个entries的资源池。而不是共享一个容量为10entries的资源池。
除了集群缓存层
缓存池更新
可以对一个活动的缓存经行容量调整。
updateResourcePools() 只允许调整堆内缓存层大小,不能更改池类型。
ResourcePools pools = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(20L, EntryUnit.ENTRIES).build();
cache.getRuntimeConfiguration().updateResourcePools(pools);
assertThat(cache.getRuntimeConfiguration().getResourcePools()
.getPoolForResource(ResourceType.Core.HEAP).getSize(), is(20L));
持久层销毁
磁盘和集群层是两种持久层。当想完全移除他们时, PersistentCacheManager 提供以下方法来来完成。
destroy()
该方法会销毁所有和该Manager相关的缓存信息。为了调用该方法,该cache manager处于被关闭或为初始化状态。
destroyCache(String cacheName)
销毁指定的缓存
多层级缓存的操作时序图
多层级缓存的简化帮put和get操作
put操作
get 操作
- 当put entry时,直接存放到可靠层,即最底层存储层。
- 使用get 时,会将 entry 从最底层往上推,这样每层都有该值。
- 为了尽快的将值放到 authoritative 层, 所有高层级的缓存层都失效了(中间层的所有缓存信息都是失效,需要get时一层一层重新加载)。
- cache miss 总会将请求打到可靠层,有点缓存击穿的意思。
即涉及更新、保存时,最底层之上的缓存层缓存信息都会失效,需要再次查询时,需要从最底层来获取缓存信息,从而将缓存信息从最底层往上一层一层的推。
所有的缓存信息在最底层缓存层都存在,中间层的缓存需要get时,从最底层一步一步往上写。
authoritative 层越慢,写入越慢,对于正常的缓存应用,其实不是个问题,因为get操作比put多得多。若不是该场景,或许第一步就不应该考虑使用缓存。
一个变通的解决方法是细分 cache,通过细粒度的定义cache,来减少cache失效的问题。
标签:10,存储,缓存,MemoryUnit,ResourcePoolsBuilder,ehcache3,分层,heap,class From: https://www.cnblogs.com/hhan/p/16638832.html