springboot 集成caffeine单体缓存两种方式及算法简介 (注解/手动)

1.简介

       Caffeine 是基于 JAVA 8 的高性能缓存库。并且在 spring5 (springboot 2.x) 后,spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。

       Caffeine 因为使用了 Window-TinyLFU 缓存淘汰策略,提供了一个近乎最佳的命中率。综合了 LRU 和 LFU 算法的长处,使其成为本地缓存之王。

        Least Recently Used:如果数据最近被访问过,将来被访问的概率也更高。每次访问就把这个元素放到队列的头部,队列满了就淘汰队列尾部的数据,即淘汰最长时间没有被访问的。缺点是,如果某一时刻大量数据到来,很容易将热点数据挤出缓存,留下来的很可能是只访问一次,今后不会再访问的或频率极低的数据。比如、微博爆出某明星糗事就是一个突发性热点事件。当事件结束后,可能没有啥访问量了,但是由于其极高的访问频率,导致其在未来很长一段时间内都不会被淘汰掉。

        Least Frequently Used:如果数据最近被访问过,那么将来被访问的概率也更高。也就是淘汰一定时间内被访问次数最少的数据(时间局部性原理)需要用 Queue 来保存访问记录,可以用 LinkedHashMap 来简单实现一个基于 LRU 算法的缓存。优点是,避免了 LRU 的缺点,因为根据频率淘汰,不会出现大量进来的挤压掉老的,如果在数据的访问的模式不随时间变化时候,LFU 能够提供绝佳的命中率。其缺点是,偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。

       W-TinyLFU 是 Caffeine 提出的一种全新算法,它可以解决频率统计不准确以及访问频率衰减的问题。这个方法让我们从空间、效率、以及适配举证的长宽引起的哈希碰撞的错误率上做均衡。在 W-TinyLFU 算法中,将整个缓存区划分为两大区域:Window Cache 和 Main Cache 。Window Cache 是一个标准的 LRU 缓存,只占整个缓存内存空间大小的 1% ; Main Cache 则是一个 SLRU (Segmented LRU) ,占整个缓存内存空间大小的 99% ,是缓存的主要区域。里面进一步被划分成两个区域:Probation Cache 观察区和 Protected Cache 保护区。Probation 观察区占 Main Cache 大小的 20%;而 Protected 保护区占 Main Cache 大小的 80% ,是 Main Cache 的主要区域。

2.caffeine相关配置说明

参数 类型 描述
initialCapacity integer 初始的缓存空间大小
maximumSize long 缓存的最大条数
maximumWeight long 缓存的最大权重
expireAfterAccess duration 最后一次写入或访问后,指定经过多长的时间过期
expireAfterWrite duration 最后一次写入后,指定经过多长的时间缓存过期
refreshAfterWrite duration 创建缓存或者最近一次更新缓存后,经过指定的时间间隔后刷新缓存
weakKeys boolean 打开 key 的弱引用
weakValues boolean 打开 value 的弱引用
softValues boolean 打开 value 的软引用
recordStats 开发统计功能

Caffeine提供三种数据驱逐策略:基于大小驱逐、基于时间驱逐、基于引用驱逐。

  1. maximumSize 和 maximumWeight 不可以同时使用。
  2. expireAfterWrite 和 expireAfterAccess 同时存在时,以 expireAfterWrite 为准
  3. weakValues 和 softValues 不可以同时使用。

Java中四种引用类型:

引用类型 被垃圾回收时间 用途 生存时间
强引用 Strong Reference 从来不会 对象的一般状态 JVM停止运行时终止
软引用 Soft Reference 在内存不足时 对象缓存 内存不足时终止
弱引用 Weak Reference 在垃圾回收时 对象缓存 gc运行后终止
虚引用 Phantom Reference 从来不会 可以用虚引用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知 JVM停止运行时终止

3.springboot集成caffeine

  1. 直接引入 Caffeine 依赖,然后创建 Caffeine 方法实现缓存。
  2. 引入 Caffeine 和 SpringCache 依赖,使用 SpringCache 注解方法实现缓存。(推荐)
  3. 引入jar包 版本也可根据不写 根据springboot版本走
        
        
            com.github.ben-manes.caffeine
            caffeine
            2.8.5
        
        
        
            org.springframework.boot
            spring-boot-starter-cache
            2.3.2.RELEASE
        
  • @Cacheable:先查询缓存是否存在,如果存在,则返回结果;如果不存在,则执行方法,并将结果保存在缓存中。主要用于查询操作
  • @CachePut:不检查缓存中是否存在,直接执行方法,并将结果保存在缓存中。主要用于数据新增和修改操作
  • @CacheEvict:从缓存中移除指定的key数据,如果allEntries属性为true,则清除全部;如果beforeInvocation属性为true,则在方法执行前删除。主要用于删除操作

3.1 方式一 只用Caffeine

/**
 * caffeine缓存配置 
 * - 优点:可以针对每个cache配置不同的参数,比如过期时长、最大容量(定制化配置) 
 * 可定义多个
 **/
@Configuration
public class CaffeineConfig {
    @Bean
    public Cache userDtoCache() {
        // 构建cache对象
        return Caffeine.newBuilder()
                //0) 驱逐策略:基于容量,时间,引用。
                //0.1 基于时间
                .expireAfterWrite(10, TimeUnit.MINUTES)
                //0.2.1 基于容量
                //初始容量
                .initialCapacity(100)
                .maximumSize(10_000)
//                //0.2.2 权重
//                .weigher(((key, value) -> {
//                    if (key.equals(1)) {
//                        return 1;
//                    } else {
//                        return 2;
//                    }
//                }))
                //0.3 基于引用
                //0.3.1 当进行GC的时候进行驱逐
//                .softValues()
                //0.3.2 当key和缓存元素都不再存在其他强引用的时候驱逐
//                .weakKeys()
//                .weakValues()
                .build();
    }

测试类

@SpringBootTest(classes = CaffeineApplication.class)
@RunWith(SpringRunner.class)
public class CaffeineTest{

    @Autowired
    private Cache userDtoCache;
    

    @Test
    public void test1() {
        //1) 新增
        // 添加或者更新一个缓存元素
        userDtoCache.put(1L, UserDto.builder()
                .id(1L)
                .name("迪丽热巴")
                .age(18)
                .build());

        // 查找缓存,如果缓存不存在则生成缓存元素,  如果无法生成则返回null
        UserDto user2 = userDtoCache.get(2L, key -> {
            // 根据key去数据库查询数据
            return UserDto.builder()
                    .id(key)
                    .name("柳岩")
                    .age(24)
                    .build();
        });


        //2) 查询
        // 取数据,上面的get也算
        UserDto user1 = userDtoCache.getIfPresent(1L);

        System.out.println("user1 = " + user1);
        System.out.println("user2 = " + user2);

        //3) 删除
        // 移除一个缓存元素
        userDtoCache.invalidate(1L);
        user1 = userDtoCache.getIfPresent(1L);
        System.out.println("user1 = " + user1);

        // 批量失效key
        userDtoCache.invalidateAll(Arrays.asList(1L, 2L));
        // 失效所有的key
        userDtoCache.invalidateAll();

        System.out.println("defaultGF2 = " + userDtoCache.getIfPresent("defaultGF"));
    }

3.2 加入SpringCache 通过注解方式

@Configuration
@EnableCaching
public class CaffeineConfig {
    /**
     * 配置缓存管理器
     *
     * @return 缓存管理器
     */
    @Bean("caffeineCacheManager")
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                // 设置最后一次写入或访问后经过固定时间过期
//                .expireAfterAccess(60, TimeUnit.SECONDS)
                // 初始的缓存空间大小
                .initialCapacity(100)
                // 缓存的最大条数
                .maximumSize(1000));
        return cacheManager;
    }

}
@Service
public class UserServiceImpl extends ServiceImpl implements UserService {

  @Autowired
  private UserMapper userMapper;


  @Override
  @Cacheable(key = "#id" , value = "caffeineCacheManager")
  public User selectById(Long id) {
    User user = userMapper.selectById(id);
    return user;
  }

  @Override
  @CachePut(key = "#result.id" , value = "caffeineCacheManager")
  public User addUserInfo(UserDto userDto) {
    User user = new User();
    BeanUtils.copyProperties(userDto, user);
    int insert = userMapper.insert(user);
    return user;
  }

  @Override
  @CachePut(key = "#userDto.id" , value = "caffeineCacheManager")
  public User updateUserInfo(UserDto userDto) {
    User user = new User();
    BeanUtils.copyProperties(userDto, user);
    userMapper.updateById(user);
    return user;
  }

  @Override
  @CacheEvict(key = "#id" , value = "caffeineCacheManager")
  public void deleteById(Long id) {
    UpdateWrapper updateWrapper = new UpdateWrapper();
    updateWrapper.eq("id", id);
    userMapper.delete(updateWrapper);
  }
}

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/37c1a45837.html