来搂一眼,本地缓存的王者(中)

简介: 来搂一眼,本地缓存的王者

Caffeine提供了三种定时驱逐策略:


expireAfterAccess(long, TimeUnit):在最后一次访问或者写入后开始计时,在指定的时间后过期。假如一直有请求访问该key,那么这个缓存将一直不会过期。


expireAfterWrite(long, TimeUnit): 在最后一次写入缓存后开始计时,在指定的时间后过期。expireAfter(Expiry): 自定义策略,过期时间由Expiry实现独自计算。缓存的删除策略使用的是惰性删除和定时删除。这两个删除策略的时间复杂度都是O(1)。


3. 基于引用的过期方式


Java中四种引用类型

引用类型 被垃圾回收时间 用途 生存时间
强引用 Strong Reference 从来不会 对象的一般状态 JVM停止运行时终止
软引用 Soft Reference 在内存不足时 对象缓存 内存不足时终止
弱引用 Weak Reference 在垃圾回收时 对象缓存 gc运行后终止
虚引用 Phantom Reference 从来不会 可以用虚引用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知 JVM停止运行时终止
// 当key和value都没有引用时驱逐缓存
LoadingCache<String, Object> cache = Caffeine.newBuilder()
    .weakKeys()
    .weakValues()
    .build(key -> function(key));
// 当垃圾收集器需要释放内存时驱逐
LoadingCache<String, Object> cache1 = Caffeine.newBuilder()
    .softValues()
    .build(key -> function(key));

注意:AsyncLoadingCache不支持弱引用和软引用。


Caffeine.weakKeys():使用弱引用存储key。如果没有其他地方对该key有强引用,那么该缓存就会被垃圾回收器回收。由于垃圾回收器只依赖于身份(identity)相等,因此这会导致整个缓存使用身份 (==) 相等来比较 key,而不是使用 equals()。


Caffeine.weakValues() :使用弱引用存储value。如果没有其他地方对该value有强引用,那么该缓存就会被垃圾回收器回收。由于垃圾回收器只依赖于身份(identity)相等,因此这会导致整个缓存使用身份 (==) 相等来比较 key,而不是使用 equals()。


Caffeine.softValues() :使用软引用存储value。当内存满了过后,软引用的对象以将使用最近最少使用(least-recently-used ) 的方式进行垃圾回收。由于使用软引用是需要等到内存满了才进行回收,所以我们通常建议给缓存配置一个使用内存的最大值。softValues() 将使用身份相等(identity) (==) 而不是equals() 来比较值。


Caffeine.weakValues()和Caffeine.softValues()不可以一起使用。


3. 移除事件监听

Cache<String, Object> cache = Caffeine.newBuilder()
    .removalListener((String key, Object value, RemovalCause cause) ->
                     System.out.printf("Key %s was removed (%s)%n", key, cause))
    .build();

4. 写入外部存储


CacheWriter 方法可以将缓存中所有的数据写入到第三方。

LoadingCache<String, Object> cache2 = Caffeine.newBuilder()
    .writer(new CacheWriter<String, Object>() {
        @Override public void write(String key, Object value) {
            // 写入到外部存储
        }
        @Override public void delete(String key, Object value, RemovalCause cause) {
            // 删除外部存储
        }
    })
    .build(key -> function(key));

如果你有多级缓存的情况下,这个方法还是很实用。

注意:CacheWriter不能与弱键或AsyncLoadingCache一起使用。


5. 统计


与Guava Cache的统计一样。

Cache<String, Object> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .recordStats()
    .build();

通过使用Caffeine.recordStats(), 可以转化成一个统计的集合. 通过 Cache.stats() 返回一个CacheStats。CacheStats提供以下统计方法:

hitRate(): 返回缓存命中率
evictionCount(): 缓存回收数量
averageLoadPenalty(): 加载新值的平均时间

3. SpringBoot 中默认Cache-Caffine Cache


SpringBoot 1.x版本中的默认本地cache是Guava Cache。在2.x(Spring Boot 2.0(spring 5) )版本中已经用Caffine Cache取代了Guava Cache。毕竟有了更优的缓存淘汰策略。


下面我们来说在SpringBoot2.x版本中如何使用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>
    <version>2.6.2</version>
</dependency>

2. 添加注解开启缓存支持

添加@EnableCaching注解:

@SpringBootApplication
@EnableCaching
public class SingleDatabaseApplication {
    public static void main(String[] args) {
        SpringApplication.run(SingleDatabaseApplication.class, args);
    }
}

3. 配置文件的方式注入相关参数


properties文件

spring.cache.cache-names=cache1
spring.cache.caffeine.spec=initialCapacity=50,maximumSize=500,expireAfterWrite=10s

或Yaml文件

spring:
  cache:
    type: caffeine
    cache-names:
    - userCache
    caffeine:
      spec: maximumSize=1024,refreshAfterWrite=60s

如果使用refreshAfterWrite配置,必须指定一个CacheLoader.不用该配置则无需这个bean,如上所述,该CacheLoader将关联被该缓存管理器管理的所有缓存,所以必须定义为CacheLoader<Object, Object>,自动配置将忽略所有泛型类型。

import com.github.benmanes.caffeine.cache.CacheLoader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author: rickiyang
 * @date: 2019/6/15
 * @description:
 */
@Configuration
public class CacheConfig {
    /**
     * 相当于在构建LoadingCache对象的时候 build()方法中指定过期之后的加载策略方法
     * 必须要指定这个Bean,refreshAfterWrite=60s属性才生效
     * @return
     */
    @Bean
    public CacheLoader<String, Object> cacheLoader() {
        CacheLoader<String, Object> cacheLoader = new CacheLoader<String, Object>() {
            @Override
            public Object load(String key) throws Exception {
                return null;
            }
            // 重写这个方法将oldValue值返回回去,进而刷新缓存
            @Override
            public Object reload(String key, Object oldValue) throws Exception {
                return oldValue;
            }
        };
        return cacheLoader;
    }
}

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月前
|
缓存 NoSQL Java
手撸的 SpringBoot缓存系统,性能杠杠的
手撸的 SpringBoot缓存系统,性能杠杠的
34 0
|
8月前
|
缓存 数据库
缓存三问
缓存三问
23 0
|
8月前
|
Java 程序员
终于不慌内卷了,多亏阿里内部的并发图册+JDK源码速成笔记
并发编程 Java并发在近几年的面试里面可以说是面试热点,每个面试官面试的时候都会跟你扯一下并发,甚至是高并发。面试前你不仅得需要弄清楚的是什么是并发,还得搞清什么是高并发! 在这里很多小白朋友就会很疑惑:我工作又不用,为啥面试总是问?真就内卷卷我呗!(手动狗头)互联网内卷已经是现在的行业趋势,而且是不可逆的,这个大家也知道;但LZ要说的是,虽然简单地增删改查并不需要并发的知识,但是业务稍微复杂一点,你的技术水平稍微提升一点的话你就会知道,并发是我们Java程序员绕不开的一道坎。
38 0
|
SQL 缓存 NoSQL
踩坑:以为是Redis缓存没想到却是Spring事务!
最近碰到了一个Bug,折腾了我好几天。并且这个Bug不是必现的,出现的概率比较低。一开始我以为是旧数据的问题,就让测试重新生成了一下数据,重新测试。由于后面几轮测试均未出现,我也就没太在意。
踩坑:以为是Redis缓存没想到却是Spring事务!
|
11月前
|
存储 缓存 NoSQL
如何设计一个超牛逼的本地缓存,太香了
最近在看Mybatis的源码,刚好看到缓存这一块,Mybatis提供了一级缓存和二级缓存;一级缓存相对来说比较简单,功能比较齐全的是二级缓存,基本上满足了一个缓存该有的功能;当然如果拿来和专门的缓存框架如ehcache来对比可能稍有差距;本文我们将来整理一下实现一个本地缓存都应该需要考虑哪些东西。
|
11月前
|
缓存 Java 程序员
肝到头秃!百度强推并发编程笔记我爱了,原来这才叫并发
随着Java程序员的大幅增长,人们对Java程序员的要求也是越来越严苛。从现在Java岗的招聘需求来看,并发编程已经是我们Java程序员避不开的坎了! 编写正确的程序并不容易,而编写正确的并发程序就更难了。与顺序执行的程序相比,并发程序中显然更容易出现错误。而且并发性错误通常并不会以某种确定的方式显现出来。
|
算法 NoSQL API
到底该不该看源码(懂这三点儿就够了)
1、不要为了看源码而看源码 2、代码积累到一定程度,遇到问题自然就去查源码了,然后你就看懂了 3、两年内不要刻意去看源码,可以点开简单了解一下就行,前两年疯狂做项目就行了,后期项目做的多了,你自己就会有疑问,每次写代码就会问自己为什么要这样写?底层的原理是什么?很自觉的带着问题就去看源码了,如果你没有这样的疑问,那说明你也不适合去看源码了,写写业务代码,了了一生
157 0
|
前端开发 Java Linux
当 Swagger 遇上 Torna,瞬间高大上了
Swagger作为一款非常流行的API文档生成工具,相信很多小伙们都在用!用多了可能会觉得它界面丑、功能弱。今天给大家推荐一款工具Torna,配合Swagger使用可以搭建界面漂亮、功能强大的API文档网站,希望对大家有所帮助! SpringBoot实战电商项目mall(50k+star)地址:github.com/macrozheng/…
|
开发框架 Java 测试技术
【测试基础】五、这样提bug单,开发小哥还会怼你么?
【测试基础】五、这样提bug单,开发小哥还会怼你么?
【测试基础】五、这样提bug单,开发小哥还会怼你么?
|
消息中间件 存储 NoSQL
三歪吐血总结了各个中间件是如何实现持久化的
到目前为止,三歪也已经接触到了不少的中间件了,比如说「Elasticsearch」「Redis」「HDFS」「Kafka」「HBase」等等。 可以发现的是,它们的持久化机制都差不得太多。今天想来总结一下,一方面想来回顾一下这些组件,一方面给还没入门过这些中间件的同学总结一下持久化的”套路“,后面再去学习的时候就会轻松很多。
129 0
三歪吐血总结了各个中间件是如何实现持久化的