本地緩存解決方案-Caffeine Cache


1.1 關於Caffeine Cache

​ Google Guava Cache是一種非常優秀本地緩存解決方案,提供了基於容量,時間和引用的緩存回收方式。基於容量的方式內部實現采用LRU算法,基於引用回收很好的利用了Java虛擬機的垃圾回收機制。其中的緩存構造器CacheBuilder采用構建者模式提供了設置好各種參數的緩存對象,緩存核心類LocalCache里面的內部類Segment與jdk1.7及以前的ConcurrentHashMap非常相似,都繼承於ReetrantLock,還有六個隊列,以實現豐富的本地緩存方案。
​ 通俗的講,Guva是google開源的一個公共java庫,類似於Apache Commons,它提供了集合,反射,緩存,科學計算,xml,io等一些工具類庫。cache只是其中的一個模塊。使用Guva cache能夠方便快速的構建本地緩存。

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

CaffeineCacheManager將會自動配置。

1.1.1 為什么要用本地緩存

相對於IO操作
速度快,效率高
相對於Redis
Redis是一種優秀的分布式緩存實現,受限於網卡等原因,遠水救不了近火

DB + Redis + LocalCache = 高效存儲,高效訪問

訪問速度和花費的關系如下圖所示:

1.1.2 什么時候用
  • 願意消耗一些內存空間來提升速度
  • 預料到某些鍵會被多次查詢
  • 緩存中存放的數據總量不會超出內存容量
1.1.3 怎么用
  1. 設置緩存容量
  2. 設置超時時間
  3. 提供移除監聽器
  4. 提供緩存加載器
  5. 構建緩存

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 添加依賴
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
        </dependency>
1.2.2.2 配置類

引入 CaffeineCache的配置文件 CaffeineCacheConfig

@Configuration
@EnableCaching
public class CaffeineCacheConfig {
    @Bean
    public CacheManager cacheManager(){
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        //Caffeine配置
        Caffeine<Object, Object> 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
@Mapper
public interface UserMapper {

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

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

    @Select("select * from tbl_user where name=#{userName}")
    List<User> getUserByName(String userName);
}
1.2.2.6 service
@Service
public class UserService{
    @Autowired
    private UserMapper userMapper;

    public List<User> getUsers() {
        return userMapper.getUsers();
    }

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

    @Cacheable(value = "user",key = "#userName")
    public List<User> getUserByName(String userName) {
        List<User> users=userMapper.getUserByName(userName);
        System.out.println("從數據庫中讀取,而非從緩存讀取!");
        return users;
    }


}

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

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

    @Autowired
    CacheManager cacheManager;

    @PostMapping("/getuserbyname")
    public List<User> getUserByName(@RequestBody User user){
        System.out.println("------------");
        System.out.println("call  /getuserbyname");
        List<User> 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按鈕后,顯示返回結果

查看IDEA控制台輸出

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM