Spring Boot —— Caffeine(内存缓存器)

发布时间 2023-12-21 11:31:28作者: 山河永慕~

项目中需要用一个替代concurrenthashmap 能够帮忙过期或者防止一直put oom所以使用

 

优点
内存管理优化
Caffeine 使用了一种基于堆外内存的存储模型,通过直接内存访问,避免了 Java 堆内存的垃圾回收开销。这种内存管理优化可以减少垃圾回收对应用性能的影响,提供更高的缓存读写性能。
高效的缓存策略
Caffeine 提供了多种缓存策略,包括基于容量、基于时间、基于引用等。这些策略可以根据应用的需求进行灵活配置,以获得最佳的缓存性能和命中率。
并发性能优化
Caffeine 在并发访问时表现出色。它采用了细粒度的锁机制和无锁的数据结构,有效地减少了并发冲突的影响,并提供了较低的锁竞争和高并发的访问性能。
功能丰富
Caffeine 提供了丰富的功能,例如异步加载、缓存监听器、过期策略、缓存统计等。这些功能可以帮助开发者更好地控制和管理缓存,满足各种业务需求。
对比
Guava Cache 相比
Caffeine 在读写性能、并发性能、内存管理方面有明显的提升,并提供了更多的配置选项和功能扩展。
Ehcache 相比
Caffeine 在读写性能、并发性能、内存管理方面也表现出更好的性能,并且具有更灵活的缓存策略和配置选项。
Redis 等远程缓存存储进行比较时
Caffeine 作为本地缓存库具有更低的访问延迟和更高的吞吐量,适用于高性能的本地缓存场景。
适用场景
高并发读写
Caffeine 在并发读写的场景下具有出色的性能表现。如果你的应用需要处理大量并发的读写操作,例如缓存数据的读取和更新,Caffeine 可以提供高吞吐量和低延迟的访问性能。

低延迟要求
Caffeine 采用了一些优化技术,如直接内存访问和细粒度的锁机制,以降低访问延迟。如果你的应用对于低延迟的响应非常关键,例如实时数据查询或高频率的请求处理,Caffeine 可以提供快速的缓存访问,以满足这些要求。

内存敏感应用
Caffeine 提供了高效的内存管理策略,可以优化内存使用并减少垃圾回收的开销。如果你的应用在内存敏感的环境中运行,例如云计算平台或资源受限的设备,Caffeine 可以帮助减少内存占用并提高应用的可伸缩性。

热数据缓存
Caffeine 提供了多种缓存策略,包括基于容量、基于时间和基于引用的策略。这使得 Caffeine 在热数据缓存方面非常适用,可以根据数据的访问频率和最近使用时间来调整缓存策略,以提高热数据的命中率。

本地缓存需求
Caffeine 是一个本地缓存库,适用于需要在应用内部管理和存储数据的场景。相比于远程缓存方案,本地缓存具有更低的访问延迟和更高的吞吐量,特别适用于需要快速访问和频繁更新的数据。

总体而言,Caffeine 适用于需要高性能、低延迟、内存敏感以及本地缓存需求的应用场景。它在处理并发访问、内存管理、缓存策略和性能优化方面具有优势,并提供了丰富的功能和配置选项,能够满足各种应用需求。

实战
Github 链接

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <!-- jdk8用2.x.x -->
    <!-- jdk11+用3.x.x -->
    <!-- 版本3.x.x是通过java11编译的 -->
    <version>2.9.3</version>
</dependency>

工具类

public class CaffeineUtil {

    /**
     * 缓存的最大容量
     */
    private static final int MAXIMUM_SIZE = 1000;

    /**
     * 缓存项的写入后过期时间
     */
    private static final int EXPIRE_AFTER_WRITE_DURATION = 30;

    /**
     * 过期时间单位(分钟)
     */
    private static final TimeUnit EXPIRE_AFTER_WRITE_TIMEUNIT = TimeUnit.MINUTES;

    private static Cache<String, Object> cache;

    /**
     * 初始化Caffeine缓存配置
     */
    static {
        cache = Caffeine.newBuilder()
                .maximumSize(MAXIMUM_SIZE)
                .expireAfterWrite(EXPIRE_AFTER_WRITE_DURATION, EXPIRE_AFTER_WRITE_TIMEUNIT)
                .build();
    }

    /**
     * 获取缓存值
     *
     * @param key 缓存键
     * @return 缓存值
     */
    public static Object get(String key) {
        return cache.getIfPresent(key);
    }

    /**
     * 设置缓存值
     *
     * @param key   缓存键
     * @param value 缓存值
     */
    public static void put(String key, Object value) {
        cache.put(key, value);
    }

    /**
     * 移除缓存项
     *
     * @param key 缓存键
     */
    public static void remove(String key) {
        cache.invalidate(key);
    }

    /**
     * 清空缓存
     */
    public static void clear() {
        cache.invalidateAll();
    }

    /**
     * 获取缓存中的所有值
     *
     * @return 缓存中的所有值集合
     */
    public static Collection<Object> getAllValues() {
        return cache.asMap().values();
    }

    /**
     * 清空缓存中的所有值
     */
    public static void removeAllValues() {
        cache.invalidateAll();
    }
}

使用

@Service
public class CaffeineServiceImpl implements CaffeineService {

    @Override
    public Result operate() {
        // 向缓存中放入数据
        CaffeineUtil.put("key1", "value1");
        CaffeineUtil.put("key2", "value2");

        // 从缓存中获取数据
        String value1 = String.valueOf(CaffeineUtil.get("key1"));
        String value2 = String.valueOf(CaffeineUtil.get("key2"));
        // 输出: Value 1: value1
        System.out.println("Value 1: " + value1);
        // 输出: Value 2: value2
        System.out.println("Value 2: " + value2);

        Collection<Object> values = CaffeineUtil.getAllValues();
        for (Object value : values) {
            System.out.println("所有Value:"+value);
        }

        // 从缓存中移除数据
        CaffeineUtil.remove("key2");

        // 再次获取数据
        String value2AfterInvalidation = String.valueOf(CaffeineUtil.get("key2"));
        // 输出: Value 2 after invalidation: null
        System.out.println("Value 2 after invalidation: " + value2AfterInvalidation);

        return Result.success();
    }

}

单元测试

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = CaffeineApplication.class)
public class CaffeineServiceImplTest {

    @Autowired
    private CaffeineServiceImpl caffeineService;

    @Before
    public void before() throws Exception {
    }

    @After
    public void after() throws Exception {
    }

    @Test
    public void method() {
        log.info(JSONObject.toJSONString(caffeineService.operate()));
    }

}