Ehcache 介绍(1)--Ehcache 功能特性
Ehcache 是一个开源的、基于标准的缓存工具,它能提升性能、减轻数据库负载并简化可扩展性。由于其稳健性、经得起考验的特点以及与其他流行框架的集成,Ehcache 成为最广泛使用的基于 Java 的缓存工具。Ehcache 从进程内缓存一直扩展到混合的进程内/进程外部署,可以处理 TB 的数据。
1、Ehcache 特性
1.1、支持多级缓存
内存
和磁盘
的二级缓存能力,Ehcache3.x 进一步扩展了此部分能力,增加了对于堆外缓存
的支持。此外,结合 Ehcache 原生支持的集群
能力,又可以打破单机的限制,完全解决容量这一制约因素。1.1.1、堆内缓存(heap)
所谓的堆内(heap)缓存,就是我们常规意义上说的内存缓存,严格意义上来说,是指被 JVM 托管占用的部分内存。内存缓存最大的优势就是具有超快的读写速度,但是不足点就在于容量有限、且无法持久化。
1.1.2、堆外缓存(off-heap)
堆外(off-heap)缓存,同样是存储在内存中。其实就是在内存中开辟一块区域,将其当做磁盘进行使用。由于内存的读写速度特别快,所以将数据存储在这个区域,读写上可以获得比本地磁盘读取更优的表现。这里的“堆外”,主要是相对与 JVM 的堆内存而言的,因为这个区域不在 JVM 的堆内存中,所以叫堆外缓存。这块的关系如下图:
既然都是内存中存储,那为何多此一举非要将其划分为堆外缓存呢?直接将这部分的空间类驾到堆内缓存上,不是一样的效果吗?原因如下:
JVM 会基于 GC 机制自动的对内存中不再使用的对象进行垃圾回收,而 GC 的时候对系统性能的影响是非常大的。堆内缓存的数据越多,GC 的压力就会越大,对系统性能的影响也会越明显。所以为了降低大量缓存对象 GC 回收动作的影响,便出现了 off-heap 处理方式。在 JVM 堆外的内存中开辟一块空间,可以像使用本地磁盘一样去使用这块内存区域,这样就既享受了内存的高速读写能力,又避免频繁 GC 带来的烦恼。
堆内缓存与堆外缓存的区别与各自优缺点:
堆内缓存是由 JVM 管理的,在 JVM 中可以直接以引用的形式去读取,所以读写的速度会特别高。而且 JVM 会负责其内容的回收与清理,使用起来比较“省心”。
堆外缓存是在内存中划定了一块独立的存储区域,然后可以将这部分内存当做“磁盘”进行使用。需要使用方自行维护数据的清理,读写前需要序列化与反序列化操作,但可以省去 GC 的影响。
1.1.3、磁盘缓存(disk)
当我们需要缓存的数据量特别大、内存容量无法满足需求的时候,可以使用 disk 磁盘存储来作为补充。相比于内存,磁盘的读写速度显然要慢一些、但是胜在其价格便宜,容量可以足够大。
1.1.4、集群缓存(Cluster)
作为单机缓存,数据都是存在各个进程内的,在分布式组网系统中,如果缓存数据发生变更,就会出现各个进程节点中缓存数据不一致的问题。为了解决这一问题,Ehcache 支持通过集群的方式,将多个分布式节点组网成一个整体,保证相互节点之间的数据同步。
需要注意的是,除了堆内缓存属于 JVM 堆内部,可以直接通过引用的方式进行访问,其余几种类型都属于 JVM 外部的数据交互,所以对这部分数据的读写时,需要先进行序列化与反序列化,因此要求缓存的数据对象一定要支持序列化与反序列化。
不同的缓存类型具有不同的运算处理速度,堆内缓存的速度最快,堆外缓存次之,集群缓存的速度最慢。为了兼具处理性能与缓存容量,可以采用多种缓存形式组合使用的方式,构建多级缓存来实现。组合上述几种不同缓存类型然后构建多级缓存的时候,也需要遵循几个约束:
A、多级缓存中必须有堆内缓存,必须按照 堆内缓存 < 堆外缓存 < 磁盘缓存 < 集群缓存 的顺序进行组合;
B、多级缓存中的容量设定必须遵循 堆内缓存 < 堆外缓存 < 磁盘缓存 < 集群缓存 的原则;
C、多级缓存中不允许磁盘缓存与集群缓存同时出现;
1.2、支持缓存持久化
常规的基于内存的缓存都有一个通病就是无法持久化,每次重启的时候,缓存数据都会丢失,需要重新去构建。而 Ehcache 支持使用磁盘来对缓存内容进行持久化保存。
1.3、支持分布式缓存
对于分布式系统,或者是集群场景下,并非是本地缓存的主战场。为了保证集群内数据的一致性,很多场景往往直接选择 Redis 等集中式缓存。但是集中式缓存也弊端,比如有些数据并不怎么更新、但是每个节点对其依赖度却非常高,如果频繁地去 Redis 请求交互,又会导致大量的性能损耗在网络 IO 交互处理上。
针对这种情况,Ehcache 给出了一个相对完美的答案:本地 + 集群化策略。即在本地缓存的基础上,将集群内各本地节点组成一个相互连接的网,然后基于某种机制,将一个节点上发生的变更同步给其余节点进行同步更新,这样就可以实现各个节点的缓存数据一致。
Ehcache 提供了多种不同的解决方案,可以将其由本地缓存变身为“分布式缓存”:
RMI 组播方式
JMS 消息方式
Cache Server 模式
JGroup 方式
Terracotta 方式
1.4、更灵活和细粒度的过期时间设定
Guava Cache 与 Caffeine,它们支持设定过期时间,但是仅允许设定缓存容器级别的过期时间,容器内的所有元素都遵循同一个过期时间。
Ehcache 不仅支持缓存容器级别的过期时间设定,还会支持为容器中每一条缓存记录设定独立过期时间,允许不同记录有不同的过期时间。这在某些场景下还是非常友好的,可以指定部分热点数据一个相对较长的过期时间,避免热点数据因为过期导致的缓存击穿。
1.5、同时支持 JCache 与 SpringCache 规范
Ehcache 作为一个标准化的通用缓存框架,同时支持 Java 目前最为主流的两大缓存标准,即官方的 JSR107 标准以及使用非常广泛的 Spring Cache 标准,这样使得业务中可以基于标准化的缓存接口去调用,避免了 Ehcache 深度耦合到业务逻辑中去。
1.6、Ehcache、Caffeine、Redis 如何选择
1.6.1、Caffeine
A、更加轻量级,使用更加简单,可以理解为一个增强版的 HashMap
B、足够纯粹,适用于仅需要本地缓存数据的常规场景,可以获取到绝佳的命中率与并发访问性能
1.6.2、Redis
A、纯粹的集中缓存,为集群化、分布式多节点场景而生,可以保证缓存的一致性
B、业务需要通过网络进行交互,相比与本地缓存而言性能上会有损耗
1.6.3、Ehcache
A、支持多级缓存扩展能力。通过内存+磁盘等多种存储机制,解决缓存容量问题,适合本地缓存中对容量有特别要求的场景
B、支持缓存数据持久化操作。允许将内存中的缓存数据持久化到磁盘上,进程启动的时候从磁盘加载缓存数据到内存中
C、支持多节点集群化组网。可以将分布式场景下的各个节点组成集群,实现缓存数据一致,解决缓存漂移问题
相比而言,Caffeine 专注于提供纯粹且简单的本地基础缓存能力;Redis 则聚焦集中缓存可保证数据的一致性;Ehcache 的功能比较中庸,介于两者之间,既具有本地缓存无可比拟的性能优势,又兼具分布式缓存的多节点数据一致性与容量扩展能力。项目里面进行选型的时候,可以结合上面的差异点,评估下自己的实际诉求,决定如何选择。
简单来说,把握如下原则即可:
A、如果只是本地简单、少量缓存数据使用的,选择 Caffeine
B、如果本地缓存数据量较大、内存不足需要使用磁盘缓存的,选择 EhCache
C、如果是大型分布式多节点系统,业务对缓存使用较为重度,且各个节点需要依赖并频繁操作同一个缓存,选择 Redis