Ehcache 是一个开源的高性能缓存,拥有很高的拓展性和伸缩性,广泛使用各种 Java 项目中(如 Hibernate 默认使用 Ehcache作为二级缓存),在目前基于 Java 的缓存方案里,几乎是性能最高的实现。
Ehcache 官网:http://www.ehcache.org
Ehcache 3.X 技术文档:http://www.ehcache.org/documentation/
Ehcache架构:
Ehcache的特点:
- 缓存数据有三级:内存、堆外缓存Off-Heap、Disk缓存,因此无需担心容量问题。还可以通过RMI、可插入API等方式进行分布式缓存。
- 缓存数据会在虚拟机重启的过程中写入磁盘,持久化。
- 具有缓存和缓存管理器的侦听接口。
- 支持多缓存管理器实例,以及一个实例的多个缓存区域。
Ehcache3支持堆、堆外、磁盘以及集群缓存;除了堆之外的三种缓存,缓存的键值对必须支持序列化和反序列化。我们在使用的时候,可以单独使用任意一个。
- heap :堆上存储,利用Java的堆上RAM内存来存储缓存条目。该层使用与Java应用程序相同的堆内存
- off-heap: 堆外内存,大小仅受可用RAM限制。不受Java垃圾回收(GC)的约束。与堆上存储相比,它速度较慢,因为在存储和重新访问JVM堆时必须将数据移入和移出JVM堆。
- disk:利用磁盘(文件系统)存储缓存条目。存储空间充足,但比基于RAM的存储要慢得多。对于所有使用磁盘存储的应用程序,建议使用快速专用磁盘来优化吞吐量。
- clustered:群集存储,该数据存储是远程服务器上的缓存。
使用
- 常被查询、最重要、数据量较小的数据存放在堆缓存,不用担心JVM的重启,有持久化机制;
- 常被查询、数据量中等的数据存放在堆外缓存,几个G就好了,不用担心服务器的重启,有持久化机制;
- 不常用、大量的数据、但又不想占用数据库IO的数据,放在Disk缓存,容量自便;
如果要使用多个层,则必须遵守一些约束条件:
- 必须始终有堆内存
- disk和clusterd不能同时存在
- 层的大小应采用金字塔式的大小,即,金字塔较高的层配置为使用的内存比下方较低的层少。
可能出现的层组合:
- heap + offheap
- heap + offheap + disk
- heap + offheap + clustered
- heap + disk
- heap + clustered
注意:
- 将值放入高速缓存时,它会直接进入最低层,比如heap + offheap + disk直接会存储在disk层。
- 当获取一个值,从最高层获取,如果没有继续向下一层获取,一旦获取到,会向上层推送,同时上层存储该值。
小知识:堆外缓存 |
---|
堆外缓存(off-heap)既是独立的进程缓存,又是JVM堆外的系统缓存。JVM堆是非常宝贵的,如果占用过大会带来GC性能问题,堆外缓存很好的解决了这个问题。现在服务器内存越来越大,而磁盘缓存的io性能又比较低,off-heap缓存就是折中的方案,既保证了高速性能,又可以有一定的容量。off-heap缓存性能的占用主要是序列化、反序列化的过程。一旦对象被序列化,在返回Java堆的时候必需反序列化才可以使用。这是一笔较大的性能开销。但off-heap还是要比本地磁盘、网络存储、RDBMS数据库IO等记录数据的系统要快的多。还应指出的是,序列化/反序列化的性能开销远没有很多用户想象的那么大。off-heap已经针对字节缓冲区做了优化,本身也包含一些优化机制,可以对使用标准Java序列化的对象进行优化,能使复杂Java对象的性能提升两倍,使byte数组的性能提升四倍。 |
备注:由于JDK17的原因,xml配置方式使用Ehcache会报各种莫名其妙的问题,基本上没有研究的意义,所以下面示例只讲了Java代码的方式。
Maven依赖:<dependency>
<groupId>org.ehcachegroupId>
<artifactId>ehcacheartifactId>
<version>3.10.0version>
dependency>
<dependency>
<groupId>javax.cachegroupId>
<artifactId>cache-apiartifactId>
<version>1.1.1version>
dependency>
测试代码一:创建CacheManager的同时指定Cache
@Test
public void fun1() {
CacheManager manager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, ResourcePoolsBuilder.heap(100).build()))
.build(true);
//通过别名获取缓存
Cache<Integer, String> cache = manager.getCache("myCache", Integer.class, String.class);
cache.put(1001, "haha");
String value = cache.get(1001);
System.out.println(value);
// 移除一个给定的Cache,CacheManager不仅会删除它对Cache的引用,而且还会关闭它。
manager.removeCache("myCache");
// 关闭CacheManager提供的所有临时资源。
manager.close();
}
在cacheManager中检索Cache,需要通过别名、键类型,值类型。比如,要获得第2步中声明的缓存,您需要它的别名=“myCache”,keyType=Integer.class和valueType = String.class。出于类型安全考虑,我们要求键和值类型都要传递。如果这些和我们期望的不同CacheManager将会在应用程序生命周期的早期抛出一个ClassCastException这可以保护缓存免受随机类型的污染。
put()方法用来往Cache中添加内容,其中:第一个参数是键,第二个参数是值。键和值类型必须是与在cacheconfig容器中定义的类型相同的类型。另外,键必须是惟一的,并且只与一个值相关联。
get(key)方法用来从缓存中检索值。它只需要一个参数,这个参数是键,然后返回与该键关联的值。如果没有与该键相关联的值,则返回null。
@Test
public void fun2() {
CacheManager manager = CacheManagerBuilder.newCacheManagerBuilder()
.build(true);
//创建Cache实例
CacheConfigurationBuilder<Integer, String> builder = CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class,
ResourcePoolsBuilder.heap(10) //堆内缓存,速度最快
.offheap(2, MemoryUnit.MB) // 堆外内存,速度低于 heap,但是高于 disk
// .disk(1, MemoryUnit.GB) //磁盘缓存,速度最低,相对于 heap和off-heap,disk可以分配大量资源空间
).withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(10)));
Cache<Integer, String> cache = manager.createCache("myCache", builder);
//写缓存
cache.put(1001, "xixi");
//读缓存
String value = cache.get(1001);
System.out.println(value);
manager.removeCache("myCache");
//关闭资源
manager.close();
}
测试代码三:将数据缓存到本地硬盘上
@Test
public void fun4() {
CacheManager manager = CacheManagerBuilder
.persistence("d:/CacheData") //硬盘缓存文件位置
.builder(CacheManagerBuilder.newCacheManagerBuilder())
.build();
manager.init();
//创建Cache实例
CacheConfigurationBuilder<Integer, String> builder = CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class,
ResourcePoolsBuilder.heap(10));
Cache<Integer, String> cache = manager.createCache("myCache", builder);
//写缓存
cache.put(1001, "xixi");
//读缓存
String value = cache.get(1001);
System.out.println(value);
manager.removeCache("myCache");
//关闭资源
manager.close();
}
测试代码四:
@Test
public void fun3() {
UserManagedCache<Integer, String> cache = UserManagedCacheBuilder.newUserManagedCacheBuilder(Integer.class, String.class)
.build(false);
cache.init();
//写
cache.put(11, "aa");
//读
String value = cache.get(11);
System.out.println(value);
cache.close();
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)