目录
[1. Java缓存技术概述](#1. Java缓存技术概述)
[1.2 优势](#1.2 优势)
[1.3 应用场景](#1.3 应用场景)
[2. Java中的内置缓存实现](#2. Java中的内置缓存实现)
[2.1 通过通过HashMap和ConcurrentHashMap实现缓存](#2.1 通过通过HashMap和ConcurrentHashMap实现缓存)
[3. Java缓存框架](#3. Java缓存框架)
[3.1 Redis](#3.1 Redis)
[3.1.1 redis的简介](#3.1.1 redis的简介)
[3.1.4 Redis的工作原理](#3.1.4 Redis的工作原理)
[3.1.5 总结](#3.1.5 总结)
[3.2 Ehcache](#3.2 Ehcache)
[3.2.1 Ehcache的简介](#3.2.1 Ehcache的简介)
[3.2.2 Ehcache的简单使用](#3.2.2 Ehcache的简单使用)
[3.2.3 数据的持久化](#3.2.3 数据的持久化)
[3.2.4 总结](#3.2.4 总结)
[3.3 Caffeine](#3.3 Caffeine)
[3.3.1 Caffeine的简介](#3.3.1 Caffeine的简介)
[3.3.2 Caffeine的简单使用](#3.3.2 Caffeine的简单使用)
[3.3.3 caffeine总结](#3.3.3 caffeine总结)
[4. 报告总结](#4. 报告总结)
摘要
缓存,作为提升系统性能的关键策略,在Java编程环境中扮演着重要角色。本报告聚焦于Java缓存技术,深入探讨了其基本概念、应用场景及多样化实现。
报告首先明确了缓存的定义,并强调了其在读操作频繁、数据计算复杂及数据更新不频繁等场景下的显著优势。在Java中,缓存的实现方式多样,包括利用本地数据结构实现的本地缓存、借助Java标准库提供的缓存功能。
此外,报告还介绍了几个广受欢迎的Java缓存框架,如Redis(高性能分布式内存缓存)、Ehcache(开源Java缓存框架,支持分布式缓存)以及Caffeine(高性能Java内存缓存库,专注于快速响应和高效内存利用)。这些框架各具特色,为开发者提供了丰富的选择空间。
通过本报告的阐述,读者将全面理解Java缓存技术的核心概念、应用场景及实现方式,为优化系统性能提供有力的理论与实践支持。
- Java缓存技术概述
1.1定义
缓存是数据访问的加速器,它为数据提供了一个快速的临时栖息地,以减少数据检索的时间消耗。在Java编程中,缓存技术是提升数据处理速度和系统性能的关键工具。它利用内存这一高速存储介质,保存数据的副本,以便快速访问,避免了对较慢存储设备(如硬盘)的频繁访问。
对于需要频繁访问相同数据的应用,Java缓存技术显得尤为重要。它像一座桥梁,连接了对快速数据的需求和较慢的数据存储,减少了对数据源的直接访问,提高了数据处理的效率。
简而言之,Java中的缓存技术是一种高效的数据处理策略,它利用内存的高速访问特性,为应用程序提供了快速的数据检索服务,从而提高了系统的响应速度和用户体验。这种技术不仅减轻了系统的资源负担,还为数据的快速处理和有效利用提供了新的途径。
1.2 优势
-
提高响应速度:通过在快速的存储介质中保存数据副本,缓存减少了数据检索时间,从而加快了应用程序的响应速度。
-
减轻后端负载:缓存减少了对数据库或其他数据源的访问次数,从而减轻了后端系统的负担。
-
提升用户体验:更快的数据访问速度和更流畅的交互显著提升了用户的体验。
-
降低成本:缓存减少了对昂贵资源(如数据库查询)的依赖,有助于降低运营成本。
1.3 应用场景
1. 数据库缓存:
- 在高并发访问的系统中,数据库压力可能非常大。为了缓解数据库压力,可以使用缓存来存储常用的查询结果。当再次访问这些数据时,可以直接从缓存中读取,而无需查询数据库,从而显著提高系统的响应速度。
- 缓存还可以用于存储数据库中的临时数据,如会话信息、用户登录状态等,以避免频繁访问数据库。
- Web应用缓存:
- 在Web应用中,缓存可以用于存储静态资源(如图片、CSS、JavaScript等)和动态内容(如网页、API响应等)。
- 通过缓存静态资源,可以减少对服务器的请求次数,降低服务器负载,提高网页加载速度。
- 对于动态内容,可以使用缓存来存储重复的查询结果或计算结果,以减少数据库查询和计算的时间。
- 分布式系统缓存:
- 在分布式系统中,缓存可以用于实现分布式锁、分布式会话共享等功能。
- 通过使用分布式锁,可以确保多个进程或线程在访问共享资源时的同步性。
- 分布式会话共享则允许多个服务器共享用户的会话信息,从而提供一致的用户体验。
4. CDN缓存:
- 内容分发网络(CDN)中的缓存用于存储和分发静态内容(如图片、视频、音频等)。
- 通过将内容缓存在CDN节点上,可以缩短用户访问内容的距离和时间,提高内容的加载速度和可用性。
5. 应用层缓存:
- 在应用层,缓存可以用于存储应用程序的临时数据、计算结果或中间状态。
- 这有助于减少应用程序对后端服务的请求次数,提高应用程序的响应速度和性能。
- 硬件缓存:
-
在计算机硬件中,缓存(如CPU缓存、硬盘缓存等)用于加速数据的访问速度。
-
通过将常用数据存储在离处理器更近的缓存中,可以减少对慢速存储设备的访问次数,提高系统的整体性能。
-
Java中的内置缓存实现
2.1 通过通过 HashMap 和 ConcurrentHashMap 实现缓存
Java 提供了多种基础数据结构,其中 HashMap 和 ConcurrentHashMap 特别适合用于构建内存缓存。HashMap 是一个高效的哈希表实现,而 ConcurrentHashMap 则在此基础上进一步优化,专为多线程环境设计,能够提供卓越的并发访问性能。
然而,这些数据结构有一个共同的局限性:它们不支持数据持久化。因此,当应用程序重启时,所有存储在其中的缓存数据都会丢失。
在特定场景下,如果需要对某些操作进行更精细的控制,以确保其原子性,那么 ConcurrentHashMap 可能无法完全满足需求。此时,开发者可能需要考虑使用更复杂的原子操作,或者将 ConcurrentHashMap 与其他并发控制工具(如锁机制)结合使用,以实现所需的数据一致性和完整性。
以下是简单的代码实现:
public class ConcurrentMyCache {
private Map<String, Object> cache = new ConcurrentHashMap<>();
public void put(String key, Object value) {
cache.put(key, value);
}
public Object get(String key) {
return cache.get(key);
}
public void remove(String key) {
cache.remove(key);
}
public void clear() {
cache.clear();
}
public int size() {
return cache.size();
}
}
public class MyCache {
private Map<String, Object> cache = new HashMap<>();
// 向缓存中放入键值对
public void put(String key, Object value) {
cache.put(key, value);
}
// 从缓存中获取值
public Object get(String key) {
return cache.get(key);
}
// 从缓存中移除键值对
public void remove(String key) {
cache.remove(key);
}
// 清空缓存
public void clear() {
cache.clear();
}
// 获取缓存大小
public int size() {
return cache.size();
}
}
@SpringBootTest
class CacheTest {
@Test
void testCache(){
ConcurrentMyCache cache=new ConcurrentMyCache();
cache.put("key1","zhangsan");
cache.put("key2","lisi");
System.out.println("key1: " + cache.get("key1"));
System.out.println("key2: " + cache.get("key2"));
MyCache cache1=new MyCache();
cache1.put("key3","wangwu");
cache1.put("key4","lht");
System.out.println("key3: " + cache1.get("key3"));
System.out.println("key4: " + cache1.get("key4"));
}
}
- Java缓存框架
3.1 Redis
3.1.1 redis的简介
Redis是一个完全开源免费的高性能(NOSQL)的key-value数据库。它遵守BSD协议,使用ANSI C语言编写,并支持网络和持久化。Redis拥有极高的性能,每秒可以进行11万次的读取操作和8.1万次的写入操作。它支持丰富的数据类型,包括String、Hash、List、Set和Ordered Set,并且所有的操作都是原子性的。此外,Redis还提供了多种特性,如发布/订阅、通知、key过期等。Redis采用自己实现的分离器来实现高速的读写操作,效率非常高。Redis是一个简单、高效、分布式、基于内存的缓存工具,通过网络连接提供Key-Value式的缓存服务。
Redis可以通过配置文件设置密码参数,这样客户端连接到Redis服务就需要密码验证,从而提高Redis服务的安全性。
3.1.2 redis的简单使用
java
@Data
public class User implements Serializable {
private Integer id;
private String username;
private String password;
}
通过自动装配redis里面的RedisTemplate这个类里面的相关配置并且封装方法方便后期使用。
java
/**
* spring redis 工具类
*
**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
@Autowired
public RedisTemplate redisTemplate;
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
}
3.1.3 redis的主要特征
-
键值(key-value)型,value支持多种不同数据结构,功能丰富
-
单线程,每个命令具备原子性
-
低延迟,速度快(基于内存、IO多路复用、良好的编码)。
-
支持数据持久化
-
支持主从集群、分片集群,
-
支持多语言客户端
3.1.4 Redis的工作原理
-
内存存储:数据完全存储在内存中,提供快速的读写访问,时间复杂度接近O(1)。
-
****单线程架构:****采用单线程处理请求,避免了多线程带来的上下文切换和锁竞争,简化了并发控制,提高了性能。
-
****非阻塞IO:****使用多路复用IO模型,能够非阻塞地处理多个客户端请求,提高了并发处理能力。
-
****Lua脚本执行:****支持在Lua脚本中执行命令,允许用户执行复杂的逻辑和操作,增加了操作的灵活性。
3.1.5 总结
Redis是一种高效的内存键值存储系统,广泛用于缓存管理、会话保持及实时数据处理等多种场景。它的优势体现在:极快的读写速度、对数据结构的多样化支持、具备持久化功能(包括RDB快照和AOF日志)、支持分布式部署,以及提供强大的原子操作特性。
3.2 Ehcache
3.2.1 Ehcache 的简介
EhCache是一个高效的纯Java进程内缓存框架,支持单机和分布式缓存,适用于需要快速数据访问的场景。它具备简单易用、快速访问、多种缓存策略(如堆缓存、磁盘缓存、集群缓存)等优点。EhCache的缓存数据有两级,一级是内存,二级是磁盘,当内存不足时,数据可以自动溢出到磁盘,从而解决了容量问题。此外,EhCache还支持缓存数据在虚拟机重启时写入磁盘,以及通过RMI、可插入API等方式进行分布式缓存。
在Spring Boot中,EhCache可以通过配置文件和Bean注入来使用,提供了灵活的缓存策略配置,如缓存对象的最大数量、对象是否永不过期、空闲时间和存活时间等。同时,EhCache还提供了缓存和缓存管理器的侦听接口,支持多缓存管理器实例以及一个实例的多个缓存区域。
虽然EhCache在非集群环境下可能导致敏感数据更新延迟,但它非常适合高QPS场景和小量数据缓存需求。使用EhCache时,建议设置较短的过期时间以保证数据的及时更新。
3.2.2 Ehcache 的简单使用
首先,需要初始化一个缓存管理器(CacheManager),利用它来创建新的缓存或者访问已有的缓存。之后,可以在这些缓存中存储数据,或者从缓存中检索数据。
java
@Test
public void test() {
// 初始化 CacheManager
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
// 一个CacheManager可以管理多个Cache
.withCache("ehcacheDemo",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
String.class,
String.class,
// heap相当于设置数据在堆内存中存储的 个数 或者 大小
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB).build()).build())
.build(true);
// 如果 CacheManagerBuilder.build(); 如果没有传参数,需要手动调用init()
// cacheManager.init();
// 基于 CacheManager 获取 Cache对象
Cache<String, String> ehCache = cacheManager.getCache("ehcacheDemo", String.class, String.class);
// 放去缓存
ehCache.put("ehcache", "hello ehcache");
// 取
System.out.println(ehCache.get("ehcache"));
}
EhCache 提供了非常灵活和强大的配置选项,这使得它能够适应各种不同的缓存需求。
java
<config xmlns="http://www.ehcache.org/v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">
<!--定义缓存-->
<cache alias="squaredNumber"
uses-template="myTemplate"><!--缓存使用的缓存模板的名称-->
<key-type>java.lang.Integer</key-type>
<value-type>java.lang.Integer</value-type>
<heap unit="entries">10</heap>
</cache>
<!--定义缓存模板-->
<cache-template name="myTemplate">
<expiry>
<ttl unit="seconds">60</ttl><!--缓存项的过期策略,60秒过期-->
</expiry>
</cache-template>
</config>
3.2.3 数据的持久化
Ehcache还可以将数据落地本地磁盘,这样的话,当服务重启后,依然会从磁盘反序列化数据到内存中,实现数据的持久化代码如下:
java
@Test
public void test1() {
// 声明存储位置
String path = "D:\\ehcache";
// 初始化 CacheManager
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
// 设置存储位置
.with(CacheManagerBuilder.persistence(path))
// 一个CacheManager可以管理多个Cache
.withCache("ehcacheDemo",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
String.class,
String.class,
// heap相当于设置数据在堆内存中存储的 个数 或者 大小
ResourcePoolsBuilder.newResourcePoolsBuilder()
// 堆内内存
.heap(10, MemoryUnit.MB)
// 堆外内存
// off-heap大小必须 大于 heap 设置的内存大小
.offheap(15,MemoryUnit.MB)
// 磁盘存储,记得添加true,才能正常持久化,并且序列号以及反序列化
// disk大小必须 大于 off-heap 设置的内存
.disk(20,MemoryUnit.MB,true)).build())
.build(true);
// 如果 CacheManagerBuilder.build(); 如果没有传参数,需要手动调用init()
// cacheManager.init();
// 基于 CacheManager 获取 Cache对象
Cache<String, String> ehCache = cacheManager.getCache("ehcacheDemo", String.class, String.class);
// 放去缓存
ehCache.put("ehcache", "hello ehcache");
// 取
System.out.println(ehCache.get("ehcache"));
// 保证数据正常持久化不丢失,记得 close()
cacheManager.close();
}
3.2.4 总结
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。EhCache支持单机缓存和分布式缓存,分布式可以理解为缓存数据的共享,这就导致内存缓存数据量偏小。ehcache缓存非常存储和读取非常快。
3.3 Caffeine
3.3.1 Caffeine 的简介
Caffeine是一个高性能的Java缓存库,它通过精细的数据结构和高效的内存管理,确保了在高并发环境下的快速访问。它提供了丰富的配置选项,包括缓存的最大容量、数据失效策略和自动刷新等,以满足不同应用程序的需求。Caffeine支持同步和异步两种数据加载方式,同步加载会阻塞主线程直到数据加载完成,而异步加载则允许主线程在数据加载时继续执行,从而提高系统的并发性。此外,Caffeine还支持多种过期策略,如基于时间或访问频率,以及注册监听器来监控缓存的变更事件,为应用程序提供了更细粒度的控制和监视。总的来说,Caffeine是一个功能强大、易于使用的缓存解决方案,非常适合需要高性能缓存的Java应用程序。
3.3.2 Caffeine 的简单使用
- 缓存加载策略
- Cache手动创建
最普通的一种缓存,无需指定加载方式,需要手动调用put()进行加载。需要注意的是put()方法对于已存在的key将进行覆盖,这点和Map的表现是一致的。在获取缓存值时,如果想要在缓存值不存在时,原子地将值写入缓存,则可以调用get(key, k -> value)方法,该方法将避免写入竞争。调用invalidate()方法,将手动移除缓存。
在多线程情况下,当使用get(key, k -> value)时,如果有另一个线程同时调用本方法进行竞争,则后一线程会被阻塞,直到前一线程更新缓存完成;而若另一线程调用getIfPresent()方法,则会立即返回null,不会被阻塞。
java
@Test
public void test1() {
Cache<Object, Object> cache = Caffeine.newBuilder()
//初始数量
.initialCapacity(10)
//最大条数
.maximumSize(10)
//expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准
//最后一次写操作后经过指定时间过期
.expireAfterWrite(1, TimeUnit.SECONDS)
//最后一次读或写操作后经过指定时间过期
.expireAfterAccess(1, TimeUnit.SECONDS)
//监听缓存被移除
.removalListener((key, val, removalCause) -> { })
//记录命中
.recordStats()
.build();
cache.put("1","张三");
//张三
System.out.println(cache.getIfPresent("1"));
//存储的是默认值
System.out.println(cache.get("2",o -> "默认值"));
}
- Loading Cache自动创建
LoadingCache是一种自动加载的缓存。其和普通缓存不同的地方在于,当缓存不存在/缓存已过期时,若调用get()方法,则会自动调用CacheLoader.load()方法加载最新值。调用getAll()方法将遍历所有的key调用get(),除非实现了CacheLoader.loadAll()方法。使用LoadingCache时,需要指定CacheLoader,并实现其中的load()方法供缓存缺失时自动加载。
在多线程情况下,当两个线程同时调用get(),则后一线程将被阻塞,直至前一线程更新缓存完成。
java
//Loading Cache自动创建
@Test
public void test2() {
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
//创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存;refreshAfterWrite仅支持LoadingCache
.refreshAfterWrite(10, TimeUnit.SECONDS)
.expireAfterWrite(10, TimeUnit.SECONDS)
.expireAfterAccess(10, TimeUnit.SECONDS)
.maximumSize(10)
//根据key查询数据库里面的值,这里是个lamba表达式
.build(key -> new Date().toString());
loadingCache.put("1","张三");
//张三
System.out.println(loadingCache.getIfPresent("1"));
//存储的是默认值
System.out.println(loadingCache.get("2",o -> "默认值"));
}
- Async Cache异步获取
AsyncCache是Cache的一个变体,其响应结果均为CompletableFuture,通过这种方式,AsyncCache对异步编程模式进行了适配。默认情况下,缓存计算使用ForkJoinPool.commonPool()作为线程池,如果想要指定线程池,则可以覆盖并实现Caffeine.executor(Executor)方法。synchronous()提供了阻塞直到异步缓存生成完毕的能力,它将以Cache进行返回。
在多线程情况下,当两个线程同时调用get(key, k -> value),则会返回同一个CompletableFuture对象。由于返回结果本身不进行阻塞,可以根据业务设计自行选择阻塞等待或者非阻塞。
java
//Async Cache异步获取
@Test
public void test3() {
AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
.refreshAfterWrite(1, TimeUnit.SECONDS)
.expireAfterWrite(1, TimeUnit.SECONDS)
.expireAfterAccess(1, TimeUnit.SECONDS)
.maximumSize(10)
.buildAsync(key -> {
try {
// 模拟数据库查询延迟
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 模拟从数据库获取的数据
return new Date().toString();
});
// 获取缓存中的值
CompletableFuture<String> future = asyncLoadingCache.get("1");
// 当获取完成时,打印结果
future.thenAccept(System.out::println);
// 等待一段时间,确保异步加载完成
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 驱逐策略
驱逐策略在创建缓存的时候进行指定。常用的有基于容量的驱逐和基于时间的驱逐。基于容量的驱逐需要指定缓存容量的最大值,当缓存容量达到最大时,Caffeine将使用LRU策略对缓存进行淘汰;基于时间的驱逐策略如字面意思,可以设置在最后访问/写入一个缓存经过指定时间后,自动进行淘汰。
- 基于容量的驱逐(LRU)
基于容量的驱逐是指缓存在达到一定的容量后,会按照最近最少使用(Least Recently Used)的策略自动淘汰掉一些缓存项。这通常用于限制缓存占用的内存空间。
java
//基于容量驱逐
@Test
public void maximumSizeTest() throws InterruptedException {
Cache<Integer, Integer> cache = Caffeine.newBuilder()
//超过10个后会使用W-TinyLFU算法进行淘汰
.maximumSize(10)
.evictionListener((key, val, removalCause) -> {
System.out.println("淘汰缓存:key:" + key + " val:" + val);
})
.build();
for (int i = 1; i < 20; i++) {
cache.put(i, i);
}
Thread.sleep(500);//缓存淘汰是异步的
// 打印还没被淘汰的缓存
System.out.println(cache.asMap());
}
- 基于时间的驱逐
基于时间的驱逐是指缓存项在一定时间后自动被淘汰。可以基于最后一次写入时间或者最后一次访问时间来淘汰缓存项。
java
/**
* 访问后到期(每次访问都会重置时间,也就是说如果一直被访问就不会被淘汰)
*/
@Test
public void expireAfterAccessTest() throws InterruptedException {
Cache<Integer, Integer> cache = Caffeine.newBuilder()
.expireAfterAccess(1, TimeUnit.SECONDS)
//可以指定调度程序来及时删除过期缓存项,而不是等待Caffeine触发定期维护
//若不设置scheduler,则缓存会在下一次调用get的时候才会被动删除
.scheduler(Scheduler.systemScheduler())
.evictionListener((key, val, removalCause) -> {
log.info("淘汰缓存:key:{} val:{}", key, val);
})
.build();
cache.put(1, 2);
System.out.println(cache.getIfPresent(1));
Thread.sleep(3000);
System.out.println(cache.getIfPresent(1));//null
- 刷新机制
Caffeine 缓存库提供了灵活的刷新机制,可以在缓存项即将过期时自动刷新数据,以确保缓存数据的时效性。refreshAfterWrite()表示x秒后自动刷新缓存的策略可以配合淘汰策略使用,注意的是刷新机制只支持LoadingCache和AsyncLoadingCache。
java
//刷新机制
private static int NUM = 0;
@Test
public void refreshAfterWriteTest() throws InterruptedException {
LoadingCache<Integer, Integer> cache = Caffeine.newBuilder()
.refreshAfterWrite(1, TimeUnit.SECONDS)
//模拟获取数据,每次获取就自增1
.build(integer -> ++NUM);
//获取ID=1的值,由于缓存里还没有,所以会自动放入缓存
System.out.println(cache.get(1));// 1
// 延迟2秒后,理论上自动刷新缓存后取到的值是2
// 但其实不是,值还是1,因为refreshAfterWrite并不是设置了n秒后重新获取就会自动刷新
// 而是x秒后&&第二次调用getIfPresent的时候才会被动刷新
Thread.sleep(2000);
System.out.println(cache.getIfPresent(1));// 1
//此时才会刷新缓存,而第一次拿到的还是旧值
System.out.println(cache.getIfPresent(1));// 2
}
- 统计
Caffeine 缓存库提供了内置的统计功能,可以帮助开发者监控和调优缓存性能。通过启用统计功能,你可以收集关于缓存操作的详细信息,例如命中率、未命中率、加载次数、加载时间等。
java
//统计
@Test
public void requestCount(){
LoadingCache<String, String> cache = Caffeine.newBuilder()
//创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存;refreshAfterWrite仅支持LoadingCache
.refreshAfterWrite(1, TimeUnit.SECONDS)
.expireAfterWrite(1, TimeUnit.SECONDS)
.expireAfterAccess(1, TimeUnit.SECONDS)
.maximumSize(10)
//开启记录缓存命中率等信息
.recordStats()
//根据key查询数据库里面的值
.build(key -> {
Thread.sleep(1000);
return new Date().toString();
});
cache.put("1", "shawn");
cache.get("1");
/*
* hitCount :命中的次数
* missCount:未命中次数
* requestCount:请求次数
* hitRate:命中率
* missRate:丢失率
* loadSuccessCount:成功加载新值的次数
* loadExceptionCount:失败加载新值的次数
* totalLoadCount:总条数
* loadExceptionRate:失败加载新值的比率
* totalLoadTime:全部加载时间
* evictionCount:丢失的条数
*/
System.out.println(cache.stats());
3.3.3 caffeine总结
Caffeine是一个高性能的Java缓存库,它提供了丰富的配置选项和强大的缓存策略,包括最近最少使用(LRU)、最近最不常用(LFU)、先进先出(FIFO)等。它支持自动刷新、定时失效、大小限制和异步加载,确保了数据的时效性和缓存的高效性。Caffeine还内置了统计监控功能,帮助开发者了解缓存性能并进行调优。其线程安全、易于集成和使用,是提升Java应用性能的理想选择。
4. 报告总结
本报告深入探讨了Java缓存技术,包括其定义、优势、应用场景及实现方式。缓存通过在内存中存储数据副本,减少了对慢速存储设备的访问,从而加速了数据检索,提高了系统性能。Java提供了多种缓存实现,如利用HashMap和ConcurrentHashMap构建本地缓存,以及使用Redis、Ehcache和Caffeine等缓存框架。这些框架支持不同的缓存策略,如堆缓存、磁盘缓存、集群缓存,以及自动刷新和过期策略。Caffeine特别受到关注,它是一个高性能的Java缓存库,提供了丰富的配置选项,包括定时失效、大小限制、同步和异步加载,以及多种过期策略。Caffeine还内置了统计监控功能,帮助开发者优化缓存性能。报告通过介绍这些技术和框架,为读者提供了理论与实践相结合的缓存解决方案,以优化系统性能。