缓存技巧 · Spring Cache && Caffeine 高性能缓存库

Caffeine 背景

Caffeine是一个高性能的Java缓存库,它基于Guava Cache进行了增强,提供了更加出色的缓存体验。Caffeine的主要特点包括:

高性能:Caffeine使用了Java 8最新的StampedLock乐观锁技术,极大地提高了缓存的并发吞吐量,使其成为一个高性能的Java缓存库。

内存友好:Caffeine支持自动驱逐缓存中的元素,以限制其内存占用。它还提供了灵活的构造器,可以创建具有不同特性的缓存,如自动加载元素、基于容量的驱逐、基于过期时间的驱逐等。

可扩展性强 :Caffeine支持 JSR-107 - JCache_(Java临时缓存API (JSR-107),也称为JCache,是定义javax.cache API的规范。 该规范是在Java社区流程下开发的,其目的是为Java应用程序提供标准化的缓存概念和机制。 API使用简单,它被设计为缓存标准,是供应商中立的。)_和Guava适配器,提高了与其他缓存库和框架的集成度。

事件监听和多种过期策略:Caffeine提供了事件监听和多种过期策略,可以更好地优化和管理数据的缓存。这些功能不仅可以提升系统的性能表现,也能够有效地降低对底层资源的压力。

本地缓存:Caffeine是一个基于Java 8开发的提供了近乎最佳命中率的高性能缓存库。可以说是目前最优秀的本地缓存。

总之,Caffeine是一个功能强大、性能卓越的Java缓存库,适用于各种需要缓存的应用场景。

Caffeine 主要特点

Caffeine 是一个高性能的 Java 缓存库,它的主要特点包括:

  1. 速度:Caffeine 的性能非常高,它的速度通常比 ConcurrentHashMap 快很多。Caffeine 使用了高效的数据结构和并发算法,以及一些优化手段,如无锁操作、缓存行填充等,来提高性能。
  2. 自动垃圾回收:Caffeine 支持基于访问时间和写入时间的自动垃圾回收。当缓存中的数据超过了设定的过期时间,Caffeine 会自动将其从缓存中移除。
  3. 基于大小的回收:Caffeine 支持基于缓存大小的回收策略。当缓存中的数据量超过了设定的最大值,Caffeine 会自动回收最近最少使用的数据。
  4. 定时回收:Caffeine 支持定时回收策略,可以设置缓存中的数据在一定时间后被强制回收。
  5. 缓存统计:Caffeine 提供了丰富的缓存统计信息,如命中率、缓存大小等,帮助开发者了解缓存的使用情况。
  6. 灵活的配置:Caffeine 提供了丰富的配置选项,允许开发者根据需要定制缓存的行为。例如,可以设置缓存的最大大小、过期时间、回收策略等。
  7. 扩展性:Caffeine 支持自定义缓存实现,开发者可以根据需要扩展 Caffeine 的功能。
  8. 与 Spring Cache 集成:Caffeine 可以很容易地与 Spring Cache 集成,使得在 Spring 项目中使用缓存变得更加简单。
  9. 无阻塞操作:Caffeine 的大部分操作都是无阻塞的,这意味着它可以在高并发环境下提供更好的性能。
  10. 轻量级:Caffeine 是一个轻量级的库,它的依赖非常少,不会给项目带来额外的负担。

这些特点使得 Caffeine 成为了一个非常受欢迎的 Java 缓存库,尤其是在需要高性能和灵活配置的场景中。如果你正在寻找一个高性能的 Java 缓存库,Caffeine 值得一试。

更多关于 Caffeine 和 Spring Cache 的信息,可以查阅官方文档:

Caffeine 使用

Spring Cache 是 Spring 框架提供的一个缓存抽象,它允许开发者通过注解的方式轻松地使用缓存。Caffeine 是一个高性能的 Java 缓存库,它提供了诸如自动垃圾回收、基于大小的回收、定时回收等功能。

要在 Spring 中使用 Caffeine 作为缓存实现,需执行以下步骤:

添加依赖

在你的项目中,添加 Caffeine 和 Spring Cache 的依赖。可以在 pom.xml 文件中添加以下依赖:

xml 复制代码
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
   <groupId>com.github.ben-manes.caffeine</groupId>
   <artifactId>caffeine</artifactId>
</dependency>

配置 Caffeine

在你的 Spring Boot 配置类中,配置 Caffeine 缓存管理器:

创建了一个 CaffeineCacheManager Bean,并设置了 Caffeine 的一些基本属性,如过期时间和最大缓存大小。

java 复制代码
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

@Configuration
public class CaffeineConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .expireAfterWrite(60, TimeUnit.SECONDS)
                .maximumSize(100));
        return cacheManager;
    }
}

可以在你的服务类中使用 Spring Cache 的注解来缓存数据。

我们使用了 @Cacheable 注解来缓存 getUserById 方法的结果。当方法被调用时,Spring 会先检查缓存中是否存在该用户,如果存在则直接返回缓存中的数据,否则才会调用方法并将结果存入缓存。例如:

java 复制代码
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable(value = "users", key = "#id")
    public User getUserById(Long id) {
        // 模拟从数据库中获取用户数据
        return new User(id, "User " + id);
    }
}

多个缓存区域

在 Caffeine 中,你可以配置多个缓存区域,每个区域都有自己的配置和缓存数据。要配置多个缓存区域,你需要为每个区域创建一个 Cache 实例,并为它们分别配置。

以下是一个使用 Spring Boot 和 Caffeine 配置多个缓存区域的例子:

配置缓存区域

在你的 Spring Boot 配置类中,配置多个缓存区域:我们创建了一个 SimpleCacheManager Bean,并为其配置了两个缓存区域。每个缓存区域都有自己的名称、过期时间和最大缓存大小。

java 复制代码
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Configuration
@EnableCaching
public class CaffeineConfig {

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        List<CaffeineCache> caches = new ArrayList<>();

        // 配置第一个缓存区域
        Cache<Object, Object> cache1 = Caffeine.newBuilder()
                .expireAfterWrite(60, TimeUnit.SECONDS)
                .maximumSize(100)
                .build();
        caches.add(new CaffeineCache("cache1", cache1));

        // 配置第二个缓存区域
        Cache<Object, Object> cache2 = Caffeine.newBuilder()
                .expireAfterWrite(30, TimeUnit.SECONDS)
                .maximumSize(50)
                .build();
        caches.add(new CaffeineCache("cache2", cache2));

        cacheManager.setCaches(caches);
        return cacheManager;
    }
}

使用缓存区域

在你的服务类中使用 Spring Cache 的注解来缓存数据,并指定要使用的缓存区域:我们使用了 @Cacheable 注解来缓存 getUserByIdFromCache1 和 getUserByIdFromCache2 方法的结果,并分别指定了不同的缓存区域。

java 复制代码
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable(value = "cache1", key = "#id")
    public User getUserByIdFromCache1(Long id) {
        // 模拟从数据库中获取用户数据
        return new User(id, "User " + id);
    }

    @Cacheable(value = "cache2", key = "#id")
    public User getUserByIdFromCache2(Long id) {
        // 模拟从数据库中获取用户数据
        return new User(id, "User " + id);
    }
}

Caffeine 常见机制

Notification on Eviction

Caffeine 提供了一种机制,允许你在缓存项被回收时接收通知,这被称为 "Notification on Eviction"。使用 removalListener 方法注册一个回调函数。当缓存项被回收时,这个回调函数会被调用。

以下是一个使用 Caffeine 的 "Notification on Eviction" 的例子:我们创建了一个 Caffeine 缓存,并使用 removalListener 方法注册了一个回调函数。当缓存项被回收时,这个回调函数会被调用,并打印出被回收的缓存项的键、值和回收原因。

java 复制代码
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;

import java.util.concurrent.TimeUnit;

public class CaffeineNotificationOnEvictionExample {

    public static void main(String[] args) throws InterruptedException {
        RemovalListener<String, String> removalListener = (key, value, cause) -> {
            System.out.println("Key: " + key + ", Value: " + value + ", Cause: " + cause);
        };

        Cache<String, String> cache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .maximumSize(100)
                .removalListener(removalListener)
                .build();

        cache.put("key1", "value1");
        cache.put("key2", "value2");

        // 等待一段时间,让缓存项过期
        Thread.sleep(60 * 1000);

        // 清理缓存
        cache.cleanUp();
    }
}

需要注意,"Notification on Eviction" 只在缓存项被主动回收时触发,而不是在缓存项被读取或写入时触发。

此外,"Notification on Eviction" 并不保证在所有情况下都能接收到通知,例如在缓存关闭或应用程序退出时,可能无法接收到通知。因此,在使用 "Notification on Eviction" 时,需要考虑到这些因素。

Cleanup

Caffeine 提供了一种机制,允许你手动触发缓存的清理操作,这被称为 "Cleanup"。使用 cleanUp 方法注册一个回调函数。当需要清理缓存时,可以调用 cleanUp 方法来触发回调函数。

以下是一个使用 Caffeine 的 "Cleanup" 的例子:我们创建了一个 Caffeine 缓存,并使用 removalListener 方法注册了一个回调函数。当缓存项被回收时,这个回调函数会被调用,并打印出被回收的缓存项的键、值和回收原因。

在这个例子中,我们使用 cleanUp 方法手动触发了缓存的清理操作。这会导致所有过期的缓存项被回收,并触发回调函数。

java 复制代码
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;

import java.util.concurrent.TimeUnit;

public class CaffeineCleanupExample {

    public static void main(String[] args) throws InterruptedException {
        RemovalListener<String, String> removalListener = (key, value, cause) -> {
            System.out.println("Key: " + key + ", Value: " + value + ", Cause: " + cause);
        };

        Cache<String, String> cache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .maximumSize(100)
                .removalListener(removalListener)
                .build();

        cache.put("key1", "value1");
        cache.put("key2", "value2");

        // 等待一段时间,让缓存项过期
        Thread.sleep(60 * 1000);

        // 手动触发缓存清理
        cache.cleanUp();
    }
}

需要注意,"Cleanup" 并不保证在所有情况下都能清理缓存。例如,在缓存关闭或应用程序退出时,可能无法清理缓存。因此,在使用 "Cleanup" 时,需要考虑到这些因素。

Enable Statistics

Caffeine 提供了一种机制,允许你启用缓存的统计信息收集功能,这被称为 "Enable Statistics"。在创建 Caffeine 缓存时,使用 recordStats 方法启用统计信息收集功能。启用统计信息收集功能后,使用 stats 方法获取缓存的统计信息。

以下是一个使用 Caffeine 的 "Enable Statistics" 的例子:我们创建了一个 Caffeine 缓存,并使用 recordStats 方法启用了统计信息收集功能。在缓存中添加了两个缓存项后,我们等待了一段时间,让缓存项过期。然后,我们使用 stats 方法获取了缓存的统计信息,并将其打印出来。

java 复制代码
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.stats.CacheStats;

import java.util.concurrent.TimeUnit;

public class CaffeineEnableStatisticsExample {

    public static void main(String[] args) throws InterruptedException {
        Cache<String, String> cache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .maximumSize(100)
                .recordStats()
                .build();

        cache.put("key1", "value1");
        cache.put("key2", "value2");

        // 等待一段时间,让缓存项过期
        Thread.sleep(60 * 1000);

        // 获取缓存的统计信息
        CacheStats stats = cache.stats();
        System.out.println("Cache Stats: " + stats);
    }
}

需要注意,启用统计信息收集功能会增加缓存的开销,因此在生产环境中使用时需要谨慎。在开发和测试环境中,启用统计信息收集功能可以帮助你更好地了解缓存的使用情况,从而优化缓存的配置和使用。

相关推荐
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
Miketutu2 小时前
Spring MVC消息转换器
java·spring
乔冠宇2 小时前
Java手写简单Merkle树
java·区块链·merkle树
LUCIAZZZ3 小时前
简单的SQL语句的快速复习
java·数据库·sql
komo莫莫da3 小时前
寒假刷题Day19
java·开发语言
小小虫码4 小时前
项目中用的网关Gateway及SpringCloud
spring·spring cloud·gateway
Linux运维老纪4 小时前
DNS缓存详解(DNS Cache Detailed Explanation)
计算机网络·缓存·云原生·容器·kubernetes·云计算·运维开发
S-X-S4 小时前
算法总结-数组/字符串
java·数据结构·算法
linwq84 小时前
设计模式学习(二)
java·学习·设计模式
桦说编程5 小时前
CompletableFuture 超时功能有大坑!使用不当直接生产事故!
java·性能优化·函数式编程·并发编程