暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

本地缓存解决方案——Caffeine Cache & Guava Cache

码酱 2021-11-13
1847

前言:介绍Caffeine Cache & Guava 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将会自动配置。

Q1:为什么要用本地缓存?

  • 相对于IO操作,速度快,效率高

  • 相对于Redis,受限于网卡等原因,远水救不了近火

  • DB + Redis + LocalCache = 高效存储,高效访问

访问速度和花费的关系如下图所示:

Q2:什么时候用到本地缓存?

  • 愿意消耗一些内存空间来提升速度

  • 预料到某些键会被多次查询(高频热点数据)

  • 缓存中存放的数据总量不会超出内存容量


Guava Cache 简单使用

1.引入依赖

<dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>18.0</version>
</dependency>

2.简单使用

   2.1提供简单的存取接口

public interface CacheService {
    //存方法
    void setCommonCache(String key,Object value);
    //取方法
    Object getFromCommonCache(String key);
}

   2.2提供实现方法

@Service
public class CacheServiceImpl implements CacheService {
 
    private Cache<String,Object> commonCache=null;
 
    @PostConstruct
    public void init(){
        commonCache= CacheBuilder.newBuilder()
                //缓存初始容量10
                .initialCapacity(10)
                //最多100个key,超过按LRU策略移除
                .maximumSize(100)
                //写入后多少秒过期
                .expireAfterWrite(60, TimeUnit.SECONDS).build();
    }
    @Override
    public void setCommonCache(String key, Object value) {
        commonCache.put(key,value);
    }
 
    @Override
    public Object getFromCommonCache(String key) {
        return commonCache.getIfPresent(key);
    }
}

   2.2使用cacheService

@RequestMapping(value = "/get",method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id")Integer id){
    //在本地缓存中查找
    ItemModel itemModel= (ItemModel) cacheService.getFromCommonCache("item_"+id);
    if(itemModel==null){
        //本地缓存没有则到redis缓存中查找
        itemModel= (ItemModel) redisTemplate.opsForValue().get("item_"+id);
        if(itemModel==null){
            //都没有则到数据库查找,找到后放入redis中
            itemModel = itemService.getItemById(id);
            redisTemplate.opsForValue().set("item_"+id,itemModel);
            redisTemplate.expire("item_"+id,10, TimeUnit.MINUTES);
        }
        //本地缓存没有时,在redis或数据库找到后再放入本地缓存
        cacheService.setCommonCache("item_"+id,itemModel);
    }
    ItemVO itemVO = convertVOFromModel(itemModel);
    return CommonReturnType.create(itemVO);

}


Caffeine Cache 简单使用

1.引入依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
      <groupId>com.github.ben-manes.caffeine</groupId>
      <artifactId>caffeine</artifactId>
</dependency>

2.引入 CaffeineCache的配置文件

@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(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不可以同时使用

3.简单使用

@Cacheable(value = "user",key = "#name")
public User getUserByName(String name) {
  User user = userMapper.getUserByName(name);
    System.err.println("从数据库获取");
    return user;
}

说明:在 getUserByName
接口上添加了注解:@Cacheable
。这是缓存的使用注解之一,除此之外常用的还有 @CachePut
和 @CacheEvit
,分别简单介绍一下:

  1. @Cacheable
    :配置在 getUserByName
    方法上表示其返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问

  2. @CachePut
    :配置于方法上时,能够根据参数定义条件来进行缓存,其与 @Cacheable
    不同的是使用 @CachePut
    标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中,所以主要用于数据新增和修改操作上

  3. @CacheEvict
    :配置于方法上时,表示从缓存中移除相应数据。

4.测试

@GetMapping("/user/{name}")
public User listUser(@PathVariable("name") String name){
    User userByName = userService.getUserByName(name);
    System.err.println("接收请求"+userByName);
    return userByName;
}

访问接口,可以看出在少于缓存失效时间时(上面设置的10s)只有第一次是从数据库中获取,缓存失效后又从数据库读取




喜欢就加个关注吧,


往期精选

#SpringCloudAlibaba系列
#SpringCloudNetflix系列
文章转载自码酱,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论