Skip to content

缓存框架

缓存框架有哪些呢?

Java中的缓存框架有多种,它们各自具有不同的特点和适用场景。以下是一些主要的Java缓存框架:

  1. Guava Cache:
    • 简介:Guava是Google提供的一套Java核心库,其中包含了一个功能强大的缓存解决方案。
    • 特点:基于LRU(最近最少使用)策略,提供了易于使用的API来创建和配置缓存。支持自动加载缓存项、缓存项过期、缓存项移除监听等功能。
    • 适用场景:适合需要高性能和简单集成的场景。
  2. Caffeine:
    • 简介:一个高性能的Java缓存库。
    • 特点:提供了比Guava Cache更高的命中率,设计用于多线程环境。通过自动调整其内部参数(如大小、过期时间等)来优化性能。支持自定义缓存策略,如LRU和LFU(最不经常使用)。
    • 适用场景:适合需要高性能缓存的场景,特别是在高并发环境下。
  3. Ehcache:
    • 简介:一个广泛使用的开源Java缓存框架。
    • 特点:支持多种缓存策略,包括LRU、LFU和FIFO(先进先出)。既可以在JVM内部使用,也可以作为集群解决方案的一部分,通过Terracotta服务器进行分布式缓存。支持持久化缓存。
    • 适用场景:适合需要丰富功能和持久化支持的场景。
  4. Hazelcast:
    • 简介:一个开源的分布式计算和数据网格平台。
    • 特点:提供了分布式和可扩展的Java缓存解决方案。可以自动管理缓存的分布式特性,如数据分区、复制和容错。还支持其他分布式数据结构,如集合、列表、映射和队列。
    • 适用场景:适合需要分布式内存数据存储的场景。
  5. Spring Cache:
    • 简介:Spring框架提供的一个缓存抽象层。
    • 特点:允许使用不同的缓存实现,如Guava Cache、Ehcache、Caffeine等。通过注解等方式简化缓存的使用。
    • 适用场景:非常适合与Spring应用程序一起使用,方便在不同缓存解决方案之间切换。
  6. Redis:
    • 简介:虽然通常被视为一个键值存储数据库,但它也经常被用作缓存系统。
    • 特点:提供了高性能的缓存解决方案,支持多种数据结构(如字符串、列表、集合、哈希表等)。具有内置的过期机制。可以配置为内存中的数据结构存储,也可以配置为持久化存储。
    • 适用场景:适合需要高性能键值存储和丰富数据结构的场景。
  7. JCache (JSR 107):
    • 简介:Java中的一套基于缓存的规范,定义了缓存的关键类型和操作方法。
    • 特点:提供了一套标准的缓存API,使得开发者可以在不同的缓存实现之间进行切换而无需修改代码。
    • 适用场景:适合需要遵循Java缓存规范、实现缓存标准化的场景。

综上所述,Java中的缓存框架种类繁多,各有千秋。在选择缓存框架时,需要根据具体的应用场景和需求进行权衡和选择。

Ehcache

详细用法请参考示例https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-spring-boot-ehcache

Caffeine

详细用法请参考示例https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-caffeine

缓存不存在则从数据库或者外部数据源加载

java
// 如果缓存中不存在会自动调用回调函数获取并自动存储到缓存中
uuidStr = UUID.randomUUID().toString();
uuidStrResult = this.caffeineInstance.get(uuidStr, key -> {
    // 如果缓存中不存在数据,这里的函数会被回调,在这里可以通过读取数据库加载数据,数据会被自动存储到 caffeine 缓存中
    return key + "-from-callback";
});
Assertions.assertEquals(uuidStr + "-from-callback", uuidStrResult);

驱逐策略

https://github.com/ben-manes/caffeine/wiki/Eviction-zh-CN

java
// region 测试驱逐策略
// https://github.com/ben-manes/caffeine/wiki/Eviction-zh-CN

// 基于容量
String uuidStr1 = UUID.randomUUID().toString();
String uuidStr2 = UUID.randomUUID().toString();
this.caffeineInstanceSizeBased.put(uuidStr1, uuidStr1);
this.caffeineInstanceSizeBased.put(uuidStr2, uuidStr2);
TimeUnit.SECONDS.sleep(1);
String uuidStrResult1 = this.caffeineInstanceSizeBased.getIfPresent(uuidStr1);
String uuidStrResult2 = this.caffeineInstanceSizeBased.getIfPresent(uuidStr2);
Assertions.assertNull(uuidStrResult1);
Assertions.assertEquals(uuidStr2, uuidStrResult2);

// 基于时间
uuidStr1 = UUID.randomUUID().toString();
uuidStr2 = UUID.randomUUID().toString();
this.caffeineInstanceTimeBased.put(uuidStr1, uuidStr1);
this.caffeineInstanceTimeBased.put(uuidStr2, uuidStr2);
TimeUnit.MILLISECONDS.sleep(600);
uuidStrResult1 = this.caffeineInstanceTimeBased.getIfPresent(uuidStr1);
uuidStrResult2 = this.caffeineInstanceTimeBased.getIfPresent(uuidStr2);
Assertions.assertEquals(uuidStr1, uuidStrResult1);
Assertions.assertEquals(uuidStr2, uuidStrResult2);
TimeUnit.MILLISECONDS.sleep(1100);
uuidStrResult1 = this.caffeineInstanceTimeBased.getIfPresent(uuidStr1);
uuidStrResult2 = this.caffeineInstanceTimeBased.getIfPresent(uuidStr2);
Assertions.assertNull(uuidStrResult1);
Assertions.assertNull(uuidStrResult2);

// endregion