跳到主要内容

Ehcache分层选项

Ehcache支持分层缓存的概念。本节介绍了不同的可用配置选项。它还说明了规则和最佳实践,以最大程度地受益于分层缓存。

有关可用存储层的一般概述,请参阅有关存储层的部分。

一、移出堆

当您在缓存中拥有除堆层之外的其他层时,会发生一些事情:

  • 向高速缓存添加映射意味着必须序列化键和值。
  • 从缓存中读取映射意味着可能必须反序列化键和值。

有了以上这两点,您需要认识到数据的二进制表示形式以及如何将其转换为串行数据以及从串行数据中转换出来的方式将在缓存性能中发挥重要作用。确保您了解可用于序列化程序的选项(请参阅“序列化程序”部分)。同样,这意味着某些配置虽然在论文上有意义,但根据应用程序的实际使用情况,可能无法提供最佳性能。

二、单层设置

所有分层选项都可以单独使用。例如,您可以使缓存中的数据仅处于异常堆中或仅处于群集中。 以下是有效的配置:

  • heap
  • offheap
  • disk
  • clustered

为此,只需在缓存配置中定义单个资源:

//首先在配置构建器中定义键和值类型。
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
//然后指定要使用的资源(层)。在这里,我们仅使用堆外资源。
ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(2, MemoryUnit.GB)).build();

2.1 堆层

由于不需要序列化,因此每个缓存的起点也更快。您可以选择使用(请参阅“序列化器和复印机”部分)按值传递键和值,默认值为按引用。 堆层可以通过条目或大小来确定大小。

//堆上仅允许10个条目。满员时将溢出。
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES);
// or 指定10个条目的快捷方式。
ResourcePoolsBuilder.heap(10);
// or 仅允许10 MB。满员时将逐出。
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB);

2.1.1 字节大小的堆

对于除了堆层之外的每个层,计算缓存的大小都非常容易。 您或多或少求和了包含序列化条目的所有字节缓冲区的大小。 当堆受大小而不是条目限制时,它会稍微复杂一些。

字节大小对运行时性能的影响取决于缓存数据的大小和图复杂度(graph complexity)。

CacheConfiguration<Long, String> usesConfiguredInCacheConfig = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
//这将限制堆层用于存储键值对的内存量。调整对象大小会产生成本。
.heap(10, MemoryUnit.KB)
// 这些设置仅由堆层使用。因此,堆外根本不会使用它。
.offheap(10, MemoryUnit.MB))
.withSizeOfMaxObjectGraph(1000)
//大小调整还可以通过2个其他配置设置进一步限制:第一个指定在移动对象图时要遍历的最大对象数(默认值:1000),
//第二个定义单个对象的最大大小(默认值:Long)。MAX_VALUE,所以几乎是无限的)。
//如果大小超过这两个限制中的任何一个,则该条目将不会存储在缓存中。
.withSizeOfMaxObjectSize(1000, MemoryUnit.B)
.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)
//除非明确定义,否则可以在CacheManager级别提供默认配置以供缓存使用。
.withDefaultSizeOfMaxObjectGraph(2000)
.withCache("usesConfiguredInCache", usesConfiguredInCacheConfig)
.withCache("usesDefaultSizeOfEngine", usesDefaultSizeOfEngineConfig)
.build(true);

2.1.2 堆外Tier

如果要使用堆外资源,则必须定义一个资源池,并提供要分配的内存大小。

//仅10 MB允许堆外。满员时将逐出
ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB);

上面的示例分配了非常少量的堆外资源。通常,您将使用更大的空间。

请记住,非堆存储的数据必须进行序列化和反序列化-因此比堆慢。因此,您应该偏爱堆外处理大量数据,因为堆上数据会对垃圾回收产生太大影响。不要忘记根据要使用的堆外大小在java选项中定义-XX:MaxDirectMemorySize选项。

2.1.3 磁盘层

对于磁盘层,数据存储在磁盘上。磁盘越快,越专用,访问数据的速度就越快。

//获得一个PersistentCacheManager,它是普通的CacheManager,但具有销毁缓存的能力。
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
//提供应该存储数据的位置。
.with(CacheManagerBuilder.persistence(new File(getStoragePath(), "myData")))
//定义将由缓存使用的磁盘的资源池。第三个参数是一个布尔值,用于设置磁盘池是否持久。设置为true时,池是持久的。当使用带有两个参数disk(long,MemoryUnit)的版本时,该池不是持久性的。
.withCache("persistent-cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder().disk(10, MemoryUnit.MB, true))
)
.build(true);

persistentCacheManager.close();

上面的示例分配了非常少量的磁盘存储。通常,您将使用更大的存储空间。 持久性意味着缓存将在JVM重新启动后幸免。重新启动JVM并在同一位置创建CacheManager磁盘持久性之后,缓存中的所有内容仍然存在。

磁盘层不能在缓存管理器之间共享。持久性目录当时专用于一个缓存管理器。

请记住,存储在磁盘上的数据必须进行序列化/反序列化,然后再写入磁盘/从磁盘读取-因此,这要比堆和堆慢。因此,在以下情况下,磁盘存储很有趣:

  • 您有大量无法容纳的数据
  • 您的磁盘比其缓存的存储速度快得多
  • 您对持久性感兴趣

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))
//定义一个OffHeapDiskStoreConfiguration实例,以指定所需的段数。
.withService(new OffHeapDiskStoreConfiguration(2))
)
.build(true);

persistentCacheManager.close();

2.1.4 集群

群集层意味着客户端正在连接到存储缓存数据的Terracotta服务器阵列。这也是在JVM之间共享缓存的一种方式。 有关使用群集层的详细信息,请参见“群集缓存”部分。

三、多层设置

如果要使用多个层,则必须遵守一些约束条件:

  • 1.多层设置中必须始终有一个堆层。
  • 2.您不能将磁盘层和群集层组合在一起。
  • 3.层的大小应采用金字塔式的大小,即金字塔较高的层配置为使用的内存少于较低层的存储量

对于1,这是当前实现的限制 对于2,此限制是必需的,因为具有两层的内容可能会超出单个JVM的寿命,可能会导致重新启动时出现一致性问题 对于3,想法是层相互关联。最快的层(堆层)在最上面,而较慢的层在下面。通常,堆比计算机的总内存受更多的限制,堆外内存比磁盘或群集上可用的内存更受限制。这导致了多层设置的典型金字塔形状。 diag 4bfd9f9e6adaf86ffbde2a2ea2fc7781

图1.层次结构 Ehcache要求堆层的大小要小于磁盘堆层的大小,并且磁盘堆层的大小必须小于磁盘层的大小。尽管Ehcache无法在配置时验证堆的基于计数的大小是否会比另一层的基于字节的大小小,但您应确保在测试期间确实如此。

考虑到上述因素,下列可能性是有效的配置:

  • heap + offheap
  • heap + offheap + disk
  • heap + offheap + clustered
  • heap + disk
  • heap + clustered

这是一个使用堆,堆和集群的示例。

PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
//群集的特定信息,告诉您如何连接到Terracotta群集
.with(cluster(CLUSTER_URI).autoCreate(c -> c))
.withCache("threeTierCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
//定义堆层,它是最小但最快的缓存层。
.heap(10, EntryUnit.ENTRIES)
//定义Offheap层。接下来是缓存层
.offheap(1, MemoryUnit.MB)
//定义集群层。此缓存的权威层
.with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 2, MemoryUnit.MB))
)
).build(true);

四、资源池

使用资源池配置层。大多数时候使用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);

这是一个使用3层(堆,堆,磁盘)的缓存。它们是使用ResourcePoolsBuilder创建和链接的。声明顺序无关紧要(例如,可以在堆之前声明offheap),因为每个层都有高度。层的高度越高,层与客户端的距离就越近

了解资源池仅指定配置是非常重要的。它不是可以在缓存之间共享的实际池。例如考虑以下代码:

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个条目。没有10个条目的共享池。池永远不会在缓存之间共享。群集缓存除外,可以共享或专用

3.1 更新资源池

可以在实时缓存上执行有限的大小调整. updateResourcePools()仅允许您更改堆层大小,而不是池类型。因此,您无法更改堆外或磁盘层的大小。

更新到http://www.ehcache.org/documentation/3.8/tiering.html#Resource+pools