Подробное объяснение структуры кэширования Ehcache

1. Введение в Ehcache

Ehcache — это широко используемая среда кэширования Java, которая может эффективно повысить производительность приложений и сократить количество взаимодействий с серверной базой данных. Он использует ряд передовых стратегий кэширования, включая кэширование в памяти, кэширование на диске, распределенное кэширование и т. д., а также предоставляет множество API и инструментов для облегчения чтения, записи и управления кэшированием.

Ehcache в основном имеет следующие характеристики:

  • Быстрота: Ehcache использует ряд эффективных стратегий кэширования для обеспечения быстрого доступа к данным, чтения и записи, тем самым повышая производительность приложений.
  • Масштабируемость: Ehcache поддерживает распределенное кэширование, которое можно легко распространить на несколько серверов, тем самым повышая отказоустойчивость и пропускную способность системы.
  • Высокая надежность: Ehcache имеет множество встроенных стратегий кэширования для поддержки сохранения и восстановления данных, а также предоставляет полный механизм обнаружения и исправления ошибок для обеспечения надежности и стабильности кэшированных данных.
  • Простота использования: Ehcache предоставляет множество API и инструментов, с помощью которых можно легко выполнить чтение, запись и управление кешем. В то же время он также интегрирован с различными фреймворками и технологиями, такими как Spring, Hibernate, MyBatis и т. д., чтобы пользователям было удобнее использовать Ecache.

2. Принцип работы

1. Запись кеша

Когда приложение записывает данные в Ehcache, Ehcache сначала проверяет, существуют ли данные уже в кэше. Если данные уже существуют в кэше, они обновляются, в противном случае данные записываются в кэш. Ниже подробно описан процесс записи кэша Ehcache.

  1. Когда приложение запрашивает запись элемента данных в Ehcache, этот элемент данных передается в API Ehcache.

  2. Ehcache сначала находит соответствующий объект Cache в соответствии с парой ключ-значение элемента данных.

  3. Ehcache в соответствии со стратегией кэширования в конфигурации, например, следует ли создавать в кэше новый элемент и будет ли новый элемент удалять старый элемент.

  4. Если необходимо создать новый элемент кэша, Ehcache создает новый элемент и добавляет его в объект Cache.

  5. Если элемент уже существует в кэше, Ehcache обновит или заменит элемент в соответствии со стратегией кэширования.

  6. Ehcache возвращает приложению обновленный объект Cache.

2. Поиск кеша

当应用程序需要查询缓存中的数据时,Ehcache 首先会检查该数据是否存在于缓存中。如果数据存在于缓存中,则直接返回缓存数据;否则,从数据库或其他资源获取数据,并将其存入缓存中。以下是 Ehcache 缓存查找的详细流程。

  1. 当应用程序请求从 Ehcache 中读取一个数据项时,这个请求被传递给Ehcache API。

  2. Ehcache首先根据该数据项的键值对定位其对应的Cache对象。

  3. Ehcache检查该数据项是否已经存在于缓存中。

  4. 如果数据项存在于缓存中,Ehcache可以直接将其返回给应用程序。

  5. 如果数据项不存在于缓存中,Ehcache就需要从数据库或其他数据源(如文件、网络等)中获取数据。

  6. 获取到数据后,Ehcache会将其添加到缓存中并返回给应用程序。

3. 缓存过期和驱逐

Ehcache 提供了多种缓存失效策略,例如基于时间的缓存失效、基于访问的缓存失效、基于大小的缓存失效等。当缓存数据过期或缓存空间不足时,Ehcache 会选择一部分缓存元素进行驱逐以腾出更多的内存空间。以下是 Ehcache 缓存过期和驱逐的详细流程。

  1. Ehcache会周期性地扫描缓存中的元素来标记那些已经过期的元素。

  2. Ehcache根据缓存策略(如基于时间、基于访问、基于大小等)判断哪些缓存元素应该被驱逐。

  3. 驱逐过程通常是异步执行的,Ehcache会在后台线程上执行这个操作。

4. 缓存持久化

Ehcache 还提供了缓存持久化功能,它可以将缓存中的数据持久化到磁盘或者其他数据源。在应用程序重启或者缓存失效后,Ehcache 可以从持久化存储中恢复数据,从而保证数据的完整性和可靠性。以下是 Ehcache 缓存持久化的详细流程。

  1. Ehcache使用磁盘存储或数据库等持久化技术来存储缓存数据。

  2. 当缓存中的数据更新时,Ehcache会自动将此数据持久化到持久化存储中。

  3. 在应用程序重启或者缓存失效后,Ehcache会从持久化存储中读取缓存数据并重新加载到内存中。

三、Ehcache 的基本使用

Spring Boot 中集成 Ehcache 缓存代码示例。

1. 添加 Ehcache 依赖

首先需要在项目的 pom.xml 文件中添加 Ehcache 缓存依赖,如下所示:

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.3</version>
</dependency>

2. 配置 Ehcache

application.ymlapplication.properties 配置文件中添加 Ehcache 的配置信息,例如:

spring:
  ehcache:
    config: classpath:ehcache.xml

在上述配置中,我们指定了 Ehcache 的配置文件为 classpath:ehcache.xml,这样 Spring Boot 在启动时会自动加载该配置文件。

接下来,需要在 src/main/resources 目录下添加 ehcache.xml 配置文件,并编写相应的 Ehcache 配置信息。例如,下面是一个简单的 Ehcache 配置文件:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.ehcache.org/v3"
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">

  <cache alias="myCache">
    <key-type>java.lang.String</key-type>
    <value-type>java.lang.String</value-type>
    <expiry>
      <ttu value="10m"/>
    </expiry>
    <heap>100</heap>
  </cache>

</config>

在上述配置中,我们定义了一个名为 myCache 的缓存,配置了键值类型、过期时间、最大存储数量等参数。

3. 使用 Ehcache

在完成 Ehcache 的配置后,就可以在 Spring Boot 中使用 Ehcache 缓存了。Spring Boot 提供了 @Cacheable@CachePut@CacheEvict 等注解来简化缓存操作,同时也支持基于缓存管理器的编程方式。

3.1 基于注解的缓存

首先介绍一下基于注解的缓存。Spring Boot 中,可以通过 @Cacheable@CachePut@CacheEvict 注解来实现对缓存的读写和删除操作。

3.1.1 @Cacheable 注解

@Cacheable 注解用于指定方法的返回值可被缓存,同时也可以指定缓存的 key 和 cacheNames,例如:

@Service
public class UserService {

    @Cacheable(cacheNames = "myCache", key = "#userId")
    public User getUserById(String userId) {
        System.out.println("get user by id: " + userId);
        return new User(userId, "张三");
    }

}

在上述代码中,我们使用 @Cacheable 注解将 getUserById() 方法返回值进行缓存,并指定了缓存的 key 为 userId,缓存名称为 myCache

3.1.2 @CachePut 注解

@CachePut 注解用于更新缓存中的数据,例如:

@Service
public class UserService {

    @CachePut(cacheNames = "myCache", key = "#user.id")
    public User updateUser(User user) {
        System.out.println("update user: " + user);
        return user;
    }

}

在上述代码中,我们使用 @CachePut 注解更新了缓存中键为 user.id 的缓存数据。

3.1.3 @CacheEvict 注解

@CacheEvict 注解用于清除缓存中的数据,例如:

@Service
public class UserService {

    @CacheEvict(cacheNames = "myCache", key = "#userId")
    public void deleteUserById(String userId) {
        System.out.println("delete user by id: " + userId);
    }

}

在上述代码中,我们使用 @CacheEvict 注解清除了缓存中键为 userId 的缓存数据。

3.2 基于缓存管理器的编程方式

除了注解方式外,还可以基于缓存管理器的编程方式来实现对缓存的读写和删除操作。Spring Boot 中,可以使用 CacheManagerCache 接口来实现缓存的管理和操作。

3.2.1 注入缓存管理器

使用基于缓存管理器的编程方式,首先需要注入使用的缓存管理器,例如:

@Service
public class UserService {

    @Autowired
    private CacheManager cacheManager;

}

在上述代码中,我们注入了 CacheManager 类型的 cacheManager 实例。

3.2.2 获取缓存实例

获取缓存实例的方式也很简单,可以通过缓存管理器的 getCache() 方法获取:

@Service
public class UserService {

    @Autowired
    private CacheManager cacheManager;

    public User getUserById(String userId) {
        Cache cache = cacheManager.getCache("myCache");
        Element element = cache.get(userId);
        if (element != null) {
            System.out.println("get user by id from cache: " + userId);
            return (User) element.getObjectValue();
        }
        System.out.println("get user by id from db: " + userId);
        User user = new User(userId, "张三");
        cache.put(new Element(userId, user));
        return user;
    }

}

在上述代码中,我们使用缓存管理器的 getCache() 方法获取名为 myCache 的缓存实例。然后,我们使用缓存实例的 get() 方法获取指定 key 对应的缓存元素,如果获取到了元素,则直接返回缓存数据;否则,从数据库中获取数据,并将其存入缓存中。

3.2.3 缓存操作

除了读取缓存数据外,我们还可以使用缓存实例的 put() 方法向缓存中添加数据,使用 remove() 方法删除缓存数据,例如:

@Service
public class UserService {

    @Autowired
    private CacheManager cacheManager;

    public User updateUser(User user) {
        Cache cache = cacheManager.getCache("myCache");
        cache.put(new Element(user.getId(), user));
        return user;
    }

    public void deleteUserById(String userId) {
        Cache cache = cacheManager.getCache("myCache");
        cache.remove(userId);
    }

}

在上述代码中,我们分别使用了缓存实例的 put()remove() 方法对缓存数据进行了更新和清除操作。

四、 Ehcache 的高级用法

1. 缓存策略

Ehcache 提供了多种缓存策略,可以根据实际需求选择合适的策略。其中,最常用的包括:

  • LRU(Least Recently Used):移除最近最少使用的缓存项。
  • LFU(Least Frequently Used):移除最不经常使用的缓存项。
  • FIFO(First In, First Out):先进先出,即移除最早加入的缓存项。
  • TTL(Time To Live):根据缓存项的过期时间来判断是否要移除该项。
  • 随机替换:随机选择一项进行移除。

在 Ehcache 中,默认使用的是 LRU 策略。如果需要更改策略,可以在 ehcache.xml 配置文件中进行设置。

2. 内存缓存和磁盘缓存

Ehcache 支持将缓存数据同时存储在内存和磁盘上,并根据实际情况调整数据的存储位置。例如,可以将经常访问的缓存数据存储在内存中,而不常使用的数据则存储在磁盘上,从而达到提高缓存效率和降低成本的目的。

ehcache.xml 配置文件中,可以通过 <heap><offheap><disk> 标签来定义缓存存储的位置。例如,下面的配置将缓存数据存储在内存中,并限制最大存储数量为 100 个:

<cache alias="myCache">
  <key-type>java.lang.String</key-type>
  <value-type>java.lang.String</value-type>
  <heap>100</heap>
</cache>

如果需要将部分缓存数据存储在磁盘上,可以添加如下配置:

<cache alias="myCache">
  <key-type>java.lang.String</key-type>
  <value-type>java.lang.String</value-type>
  <heap>10</heap> <!-- 最多存储 10 个缓存项到堆中 -->
  <offheap>1MB</offheap> <!-- 最多存储 1MB 缓存项到堆外内存中 -->
  <diskPersistent>true</diskPersistent> <!-- 开启磁盘持久化,即使程序重启后,缓存仍然有效 -->
  <diskSpoolBufferSizeMB>30</diskSpoolBufferSizeMB> <!-- 磁盘缓存区大小,缓存数据先以临时文件的形式写入磁盘缓存区,待达到一定阈值后再写入磁盘 -->
  <diskExpiryThreadIntervalSeconds>120</diskExpiryThreadIntervalSeconds> <!-- 磁盘数据过期检测间隔时间,单位为秒 -->
  <maxEntriesLocalDisk>1000</maxEntriesLocalDisk> <!-- 磁盘缓存最大存储数量 -->
</cache>

3. 分布式缓存

Ehcache 提供了分布式缓存的支持,并可以通过 RMI、JMS、Spring 等方式进行实现。在分布式环境下,每个缓存节点都可以通过网络访问远程节点上的缓存数据,从而实现数据共享和分布式缓存的目的。

ehcache.xml 配置文件中,可以添加 <cacheManagerPeerProviderFactory><cacheManagerPeerListenerFactory> 标签来定义缓存节点的配置信息。例如,下面的配置定义了一个名为 myCache 的缓存,采用了 RMI 方式实现分布式缓存:

<cache-manager-peer-provider-factory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
                                      properties="peerDiscovery=automatic,
                                                  multicastGroupAddress=230.0.0.1,
                                                  multicastGroupPort=4446,
                                                  timeToLive=32"/>

<cache-manager-peer-listener-factory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
                                      properties="port=40001,
                                                  socketTimeoutMillis=2000"/>

<cache alias="myCache">
  <key-type>java.lang.String</key-type>
  <value-type>java.lang.String</value-type>
  <diskPersistent>true</diskPersistent>
  <diskSpoolBufferSizeMB>30</diskSpoolBufferSizeMB>
  <maxEntriesLocalDisk>1000</maxEntriesLocalDisk>
  <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                              properties="replicateAsynchronously=true,
                                          replicatePuts=true,
                                          replicateUpdates=true,
                                          replicateUpdatesViaCopy=false,
                                          replicateRemovals=true"/>
</cache>

在上述配置中,我们首先定义了一个 RMI 的缓存管理器提供者工厂 RMICacheManagerPeerProviderFactory 和一个 RMI 的缓存管理器监听器工厂 RMICacheManagerPeerListenerFactory,并分别指定了自动发现、组播地址、端口等相关参数。

接着,我们定义了名为 myCache 缓存添加了一个 cacheEventListenerFactory 属性,用于定义缓存事件的处理方式,即使用 RMICacheReplicatorFactory 实现缓存数据的复制和同步。

需要注意的是,分布式缓存的配置和使用与单机缓存有所不同,同时也需要考虑到网络通信等问题,因此更加复杂和耗费资源。在选择是否需要使用分布式缓存时,需要权衡其带来的性能、复杂性和成本等因素。

Supongo que te gusta

Origin juejin.im/post/7237389821101523005
Recomendado
Clasificación