# 缓存之 Ehcache

# 简介

Ehcache 是一个功能强大的 Java 缓存库,它提供了丰富的缓存策略和灵活的配置选项。本指南将详细介绍如何使用 Ehcache 以及如何将其与 Spring Boot 集成。

# 什么是 Ehcache?

Ehcache 是一个开源的 Java 缓存,它旨在降低系统负载、提高响应速度,并且可以减少对后端数据源(如数据库)的访问。Ehcache 支持多种缓存策略,如 LRU(最近最少使用)、FIFO(先进先出)等。

# Ehcache 的主要特点

  • 简单易用:提供简单直观的 API。
  • 可扩展性:支持集群,可进行分布式缓存。
  • 多种缓存策略:支持多种缓存逐出算法。
  • 缓存监听:可以监听缓存项的创建、更新和删除。
  • 缓存数据持久化:支持将缓存数据写入磁盘。

# 同样都是缓存 redis 和 ehcache 哪个更好呢?

# Redis

  1. 内存数据库:Redis 是一个开源的内存中数据结构存储系统,它可以用作数据库、缓存和消息代理。
  2. 丰富的数据类型:Redis 支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。
  3. 持久化:Redis 支持数据的持久化,可以通过 RDB 和 AOF 两种方式将内存中的数据保存到磁盘中。
  4. 分布式:Redis 支持集群模式,能够实现数据的分片和复制,提高缓存的容量和可用性。
  5. 高并发:Redis 能够处理大量的并发请求,适合需要高并发读写的应用场景。
  6. 功能丰富:Redis 不仅可以作为缓存使用,还可以作为消息队列、排行榜、实时分析等场景的数据存储。

# Ehcache

  1. Java 进程内缓存:Ehcache 是一个纯 Java 的进程内缓存框架,简单易用。
  2. 内存和磁盘存储:Ehcache 支持在内存和磁盘上存储缓存数据,具有两级缓存机制。
  3. 多种缓存策略:Ehcache 提供了多种缓存策略,如 LRU(最近最少使用)、FIFO(先进先出)等。
  4. 分布式缓存:Ehcache 可以通过 RMI、可插入 API 等方式实现分布式缓存(比较麻烦)。
  5. 缓存监听:Ehcache 支持缓存和缓存管理器的侦听接口,可以监听缓存项的创建、更新和删除。
  6. Hibernate 缓存实现:Ehcache 是 Hibernate 的默认缓存提供者,适合与 Hibernate 集成使用。

# 哪个更好?

选择 Redis 还是 Ehcache 取决于具体的应用场景和需求:

  • 如果你需要一个功能丰富、支持多种数据结构和持久化的内存数据库,并且可以处理高并发请求,那么 Redis 可能是更好的选择。
  • 如果你需要一个简单、轻量级的 Java 进程内缓存,或者需要与 Hibernate 集成,那么 Ehcache 可能更适合你的需求。

总的来说,两者各有千秋,没有绝对的 “更好”,只有更适合特定场景的解决方案。在实际应用中,有时也会将两者结合使用,以发挥各自的优势。

也就说,我只需要简单的缓存,加快查询并且只是单机环境下,那么使用 Ehcache 就够了。 如果需要复杂的缓存结构,或者是在分布式环境下就可以考虑 redis

# 扩展

其实还可以使用 spring 自带缓存框架(用法可下面的一样,只是不需要加配置,只需要在启动类加相同的注解,使用注解的方式缓存)

spring-context-5.2.15.RELEASE.jar
在 org.springframework.cache.* 包下

# Ehcache 整合 spring boot

# 1. 添加依赖

首先,需要在项目的 pom.xml 文件中添加 Ehcache 的依赖。

<dependencies>
    <!-- Spring Boot Starter Cache -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <!-- Ehcache -->
    <dependency>
        <groupId>org.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>3.9.2</version>
    </dependency>
</dependencies>

# 2. 配置 Ehcache

src/main/resources 目录下创建一个 Ehcache 配置文件,例如 ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!--
    磁盘的缓存位置
        磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于 Windows 系统的虚拟内存
        path: 指定在硬盘上存储对象的路径
        path 可以配置的目录有:
            user.home(用户的家目录)
            user.dir(用户当前的工作目录)
            java.io.tmpdir(默认的临时目录)
            ehcache.disk.store.dir(ehcache 的配置目录)
            绝对路径(如:d:\\ehcache)
        查看路径方法:String tmpDir = System.getProperty ("java.io.tmpdir");
     -->
    <diskStore path="D:\EhCache"/>
    <!-- 默认缓存 -->
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
    <!--helloworld 缓存 -->
    <cache name="HelloWorldCache"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="5"
           timeToLiveSeconds="5"
           overflowToDisk="false"
           memoryStoreEvictionPolicy="LRU"/>
    <!--
     defaultCache:默认缓存策略,当 ehcache 找不到定义的缓存时,则使用这个
    缓存策略。只能定义一个。
     -->
    <!--
    name: 缓存名称。
    maxElementsInMemory: 缓存最大数目
    maxElementsOnDisk:硬盘最大缓存个数。
    eternal: 对象是否永久有效,一但设置了,timeout 将不起作用。
    overflowToDisk: 是否保存到磁盘,当系统宕机时
    timeToIdleSeconds: 设置对象在失效前的允许闲置时间(单位:秒)。仅当
    eternal=false 对象不是永久有效时使用,可选属性,默认值是 0,也就是可闲置时间无穷大。
    timeToLiveSeconds: 设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当 eternal=false 对象不是永久有效时使用,
                      默认是 0.,也就是对象存活时间无穷大。
    diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store
    persists between restarts of the Virtual Machine. The default value is false.
    diskSpoolBufferSizeMB:这个参数设置 DiskStore(磁盘缓存)的缓存区大小。默认是 30MB。每个 Cache 都应该有自己的一个缓冲区。
    diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是 120 秒。
    memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,
    Ehcache 将会根据指定的策略去清理内存。默认策略是 LRU(最近最少使用)。你可以设置为 FIFO(先进先出)或是 LFU(较少使用)。
    clearOnFlush:内存数量最大时是否清除。
    memoryStoreEvictionPolicy: 可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
    FIFO,first in first out,这个是大家最熟的,先进先出。
    LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个 hit 属性,hit 值最小的将
                               会被清出缓存。
    LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳
                              离当前时间最远的元素将被清出缓存。
    -->
</ehcache>

# 3. application.yml

spring:
  cache:
    type: ehcache
    ehcache:
      config: classpath:ehcache.xml

# 4. spring boot 启动类开启缓存注解

@SpringBootApplication
@EnableCaching
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

# 5. 使用 Ehcache

在需要缓存的组件中注入 CacheManager 并使用它来获取缓存。

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.springframework.stereotype.Service;
@Service
public class CacheService {
    private final Cache<String, Object> cache;
    public CacheService(CacheManager cacheManager) {
        this.cache = cacheManager.getCache("myCache", String.class, Object.class);
    }
    public Object getFromCache(String key) {
        return cache.get(key);
    }
    public void putToCache(String key, Object value) {
        cache.put(key, value);
    }
}

# 6. 缓存注解

Ehcache 也支持使用注解来简化缓存逻辑。

import org.ehcache.Cache;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class MyService {
    @Cacheable(cacheNames = "myCache", key = "#id")
    public Object getDataById(String id) {
        // 模拟耗时操作
        return "Data for " + id;
    }
}

# 实际使用场景

# 场景一:缓存数据库查询结果

假设有一个方法用于从数据库获取用户信息,我们可以使用 Ehcache 来缓存这些查询结果。

@Cacheable(value = "userCache", key = "#userId")
public User findUserById(Long userId) {
    // 数据库查询逻辑
    return userRepository.findById(userId).orElse(null);
}

# 场景二:缓存 Web 页面

对于不经常变化的 Web 页面,可以使用 Ehcache 来缓存页面内容。

@Controller
public class PageController {
    @Cacheable(cacheNames = "pageCache", key = "#pageName")
    public String showPage(String pageName) {
        // 页面渲染逻辑
        return "page";
    }
}

# 场景三:缓存计算结果

对于计算密集型的操作,可以使用 Ehcache 来缓存结果,避免重复计算。

@Cacheable(value = "computationCache", key = "#input")
public double computeValue(String input) {
    // 复杂的计算逻辑
    return computeResult(input);
}

# 总结

Ehcache 是一个功能丰富的缓存框架,它可以显著提高应用程序的性能。通过与 Spring Boot 的集成,可以轻松地在应用程序中实现缓存逻辑。根据实际业务需求选择合适的缓存策略,可以有效地减少系统负载和提高响应速度。

# 参考资料

  • Ehcache 官方文档