为什么使用缓存?
缓存能够提高应用程序性能,降低数据库成本,减少后端负载,增加可预测性能,消除数据库热点,提高读取吞吐量。
在Java应用中,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中。相对从数据库中读取来说,读取缓存效率会有效提升。
在集群环境下,常用分布式缓存有Redis、Memcached等。但在某些业务场景下,可能不需要去搭建一套复杂的分布式缓存系统,在单机环境下,通常是会希望使用内部缓存(LocalCache)
对于自研Java内存缓存有两种方案:
基于JSR107规范自研
基于ConcurrentHashMap实现数据缓存
JSR107
https://jcp.org/en/jsr/detail?id=107
JSR107目标是为应用程序提供缓存Java对象功能,定义一套通用的缓存概念和工具,最小化开发人员使用缓存学习成本,最大化应用程序在使用不同缓存实现之间的可移植性,支持进程内和分布式的缓存实现。
核心概念为Java Caching定义了5个核心接口,分别是CachingProvider,CacheManager,Cache,Entry和Expiry。
CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager,一个应用可以在运行期访问多个CachingProvider。
CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CachingProvider所拥有。
Cache是一个类似Map的数据结构并临时存储以Key为索引的值,一个Cache仅被一个CacheManager所拥有。
Entry是一个存储在Cache中的key-value对。
Expiry 每一个存储在Cache中的条目有一个定义的有效期,一旦超过这个时间,条目为过期的状态,一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
ConcurrentHashMap实现数据缓存
Guava Cache介绍
Guava Cache是Google Guava中一个内存模块,用于将数据缓存到JVM内存中。实际项目开发中经常将一些公共或者常用的数据缓存起来方便快速访问。
主要运用于愿意消耗一些内存来提升速度,预料到某些键会被查询一次以上,缓存中存放的数据总量不会超出内存容量。
Guava Cache是单个应用运行时本地缓存,它不把数据存放到文件或者外部服务器,如果这不符合项目需求,请尝试Memcached、Redis这类工具。
public class User {
private String userName;
private String key;
public User() {
}
public User(String userName, String key) {
this.userName = userName;
this.key = key;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
@Override
public String toString() {
return "{" +
"userName='" + userName + '\'' +
", key='" + key + '\'' +
'}';
}
}
import cn.zjut.cache.java.pojo.User;
import com.google.common.cache.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class GuavaCacheDemo {
public static void main(String[] args) throws ExecutionException {
LoadingCache userCache
= CacheBuilder.newBuilder()
.concurrencyLevel(8)
.expireAfterWrite(8, TimeUnit.SECONDS)
.refreshAfterWrite(1, TimeUnit.SECONDS)
.initialCapacity(10)
.maximumSize(100)
.recordStats()
.removalListener(new RemovalListener