Categories
程式開發

緩存解決方案-技術專題-Caffeine Cache


內容簡介:​

Google Guava Cache是​​一種非常優秀本地緩存解決方案,提供了基於容量,時間和引用的緩存回收方式。基於容量的方式內部實現採用LRU算法,基於引用回收很好的利用了Java虛擬機的垃圾回收機制。

其中的緩存構造器CacheBuilder採用構建者模式提供了設置好各種參數的緩存對象,緩存核心類

LocalCache裡面的內部類Segment與jdk1.7及以前的ConcurrentHashMap非常相似,都繼承於ReetrantLock,還有六個隊列,以實現豐富的本地緩存方案。​

1.1 關於Caffeine Cache

​ 通俗的講,Guava是google開源的一個公共java庫,類似於Apache Commons,它提供了集合,反射,緩存,科學計算,xml,io等一些 工具 類庫。 cache只是其中的一個模塊。使用Guava cache能夠方便快速的構建本地緩存。

​Caffeine是使用Java8對Guava緩存的重寫版本 ,在Spring Boot 2.0中將取代Guava。如果出現Caffeine,CaffeineCacheManager將會自動配置。

1.1.1 為什麼要用本地緩存

相對於IO操作=> 速度快,效率高相對於Redis => Redis是一種優秀的分佈式緩存實現,受限於網卡等原因,遠水救不了近火DB + Redis + LocalCache = 高效存儲,高效訪問訪問速度和花費的關係如下圖所示:

緩存解決方案-技術專題-Caffeine Cache 1

1.1.2 什麼時候用

願意消耗一些內存空間來提升速度預料到某些鍵會被多次查詢緩存中存放的數據總量不會超出內存容量

1.1.3 怎麼用

設置緩存容量設置超時時間提供移除監聽器提供緩存加載器構建緩存

1.2 使用Caffeine Cache

使用springboot2.x操作Caffeine Cache

搭建工程:Springboot2.x + MyBatis + MySQL + Caffeine Cache

Caffeine是使用Java8對Guava緩存的重寫版本,在Spring 5.0或者Spring Boot 2.0中將取代,基於LRU算法實現,支持多種緩存過期策略。

1.2.1 準備工作

準備好 數據庫” 和數據表並插入相應實驗數據(MySQL)

-- 新建表
create database if not exists guavach charset utf8;
-- 使用表
use guavach;
-- 创建用户表tbl_user
create table tbl_user(
id int(10) not null primary key auto_increment,
name varchar(50) not null,
age int(20) not null
)engine=innodb default charset=utf8;
-- 初始化数据
insert into tbl_user values('1','codesheep.cn','25');
insert into tbl_user values('2','hansongwang99','30');
insert into tbl_user values('3','刘能','35');
insert into tbl_user values('4','赵四','38');

1.2.2 java工程

1.2.2.1 添加依賴


com.github.ben-manes.caffeine
caffeine

1.2.2.2 配置類

引入CaffeineCache的配置文件 CaffeineCacheConfig

@Configuration
@EnableCaching
public class CaffeineCacheConfig {
@Bean
public CacheManager cacheManager(){
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
//Caffeine配置
Caffeine caffeine = Caffeine.newBuilder()
//最后一次写入后经过固定时间过期
.expireAfterWrite(10, TimeUnit.SECONDS)
//maximumSize=[long]: 缓存的最大条数
.maximumSize(1000);
cacheManager.setCaffeine(caffeine);
return cacheManager;
}
}

說明:

Caffeine配置說明:

initialCapacity =[integer]: 初始的緩存空間大小maximumSize=[long]: 緩存的最大條數maximumWeight=[long]: 緩存的最大權重expireAfterAccess=[duration]: 最後一次寫入或訪問後經過固定時間過期expireAfterWrite=[duration]: 最後一次寫入後經過固定時間過期refreshAfterWrite=[duration]: 創建緩存或者最近一次更新緩存後經過固定的時間間隔,刷新緩存weakKeys: 打開key的弱引用weakValues:打開value的弱引用softValues:打開value的軟引用recordStats:開發統計功能

注意:

expireAfterWrite和expireAfterAccess同事存在時,以expireAfterWrite為準。 maximumSize和maximumWeight不可以同時使用weakValues和softValues不可以同時使用

1.2.2.3 配置文件

server:
port: 9020
# Mysql
spring:
datasource:
url: jdbc:mysql://localhost:3306/guavach?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
# mybatis配置
mybatis:
configuration:
map-underscore-to-camel-case: true
#debug: true

1.2.2.4 實體類

public class User implements Serializable {
private static final long serialVersionUID=1L;
private Long id;
private String name;
private Integer age;

public static long getSerialVersionUID() {
return serialVersionUID;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

1.2.2.5映射器

@Mapper
public interface UserMapper {

@Select("select * from tbl_user")
List getUsers();

@Insert("insert into tbl_user values(#{name},#{age})")
int addUser(User user);

@Select("select * from tbl_user where name=#{userName}")
List getUserByName(String userName);
}

1.2.2.6服務

@Service
public class UserService{
@Autowired
private UserMapper userMapper;

public List getUsers() {
return userMapper.getUsers();
}

public int addUser(User user) {
return userMapper.addUser(user);
}

@Cacheable(value = "user",key = "#userName")
public List getUserByName(String userName) {
List users=userMapper.getUserByName(userName);
System.out.println("从数据库中读取,而非从缓存读取!");
return users;
}

}

說明:在 getUsersByName 接口上添加了註解: @Cacheable 。這是緩存的使用註解之一,除此之外常用的還有 @CachePut 和 @CacheEvit ,分別簡單介紹一下:

@Cacheable :配置在 getUsersByName 方法上表示其返回值將被加入緩存。同時在查詢時,會先從緩存中獲取,若不存在才再發起對數據庫的訪問@CachePut :配置於方法上時,能夠根據參數定義條件來進行緩存,其與@Cacheable 不同的是使用@CachePut標註的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中,所以主要用於數據新增和修改操作上@CacheEvict :配置於方法上時,表示從緩存中移除相應數據。

1.2.2.7控制器

@RestController
public class UserController {
@Autowired
private UserService userService;

@Autowired
CacheManager cacheManager;

@PostMapping("/getuserbyname")
public List getUserByName(@RequestBody User user){
System.out.println("------------");
System.out.println("call /getuserbyname");
List users = userService.getUserByName(user.getName());
return users;

}

@GetMapping("/hehe")
public String hehe(){
return "hehhehhehe";
}
}

1.2.2.8 啟動類

@SpringBootApplication
@EnableCaching//新增注解
public class Caffeinecache01Application {

public static void main(String[] args) {
SpringApplication.run(Caffeinecache01Application.class, args);
}

}

1.3 運行

啟動主類,使用postman測試

打開postman,輸入 json” 字段,點擊send按鈕後,顯示返回結果

緩存解決方案-技術專題-Caffeine Cache 2

查看IDEA控制台輸出

緩存解決方案-技術專題-Caffeine Cache 3

第一次獲取時,為從數據庫讀取

接著點擊,間隔時間少於10秒,顯示如下結果,可以看到緩存的啟用和失效時的效果如下所示(上文Guava Cache的配置文件中設置了緩存user的實效時間為10s):

緩存解決方案-技術專題-Caffeine Cache 4