caffeine本地缓存的使用和详解

在项目中我们经常使用缓存架构,来缓存我们的数据,比如redis、caffeine等。那么redis和caffeine有什么区别?作用又有哪些不同呢?

caffeine详情

redis和caffeine的区别?

相同点就不用说,广义上都是缓存的方式。咱们就说说不同。

  • redis是将数据存储到内存里;caffeine是将数据存储在本地应用里
  • caffeine和redis相比,没有了网络IO上的消耗

那么在高并发场景中,一般我们都是结合使用,形成一二级缓存。caffeine作为一级缓存,redis作为二级缓存。
使用流程大致如下:去一级缓存中查找数据(caffeine-本地应用内)如果没有的话,去二级缓存中查找数据(redis-内存)再没有,再去数据库中查找数据(数据库-磁盘)。
在这里插入图片描述

caffeine的使用

Caffeine 相当于一个缓存工厂,可以创建出多个缓存实例 Cache。这些缓存实例都继承了 Caffeine 的参数配置,Caffeine 是如何配置的,这些缓存实例就具有什么样的特性和功能。
Caffeine 是目前性能最好的本地缓存,因此,在考虑使用本地缓存时,直接选择 Caffeine 即可。

Caffeine.newBuilder().maximumSize(6000).expireAfterAccess(10, TimeUnit.MINUTES)
				.expireAfterWrite(10, TimeUnit.MINUTES).removalListener(new RemovalListener<String, CacheUserInfo>() {
    
    

					@Override
					public void onRemoval(@Nullable String key, @Nullable CacheUserInfo value,
							@NonNull RemovalCause cause) {
    
    
						if (value != null) {
    
    
							log.info("user profile has removed. uid {}, name {}", key, value.getName());
						}
					}
				}).recordStats().build(new CacheLoader<String, CacheUserInfo>() {
    
    

					@Override
					public @Nullable CacheUserInfo load(@NonNull String telephone) throws Exception {
    
    

						try {
    
    
							return CacheUserInfo.getLoggedUserProfile(telephone, false);
						} catch (Exception e) {
    
    
							log.error("load user profile error", e);
							return null;
						}
					}

				});

	}

caffeine缓存属性

initialCapacity 缓存初始容量

整数,表示能存储多少个缓存对象。
为什么要设置初始容量呢?因为如果提前能预估缓存的使用大小,那么可以设置缓存的初始容量,以免缓存不断地进行扩容,致使效率不高。

maximumSize 最大容量

如果缓存中的数据量超过这个数值,Caffeine 会有一个异步线程来专门负责清除缓存,按照指定的清除策略来清除掉多余的缓存。注意:比如最大容量是 2,此时已经存入了2个数据了,此时存入第3个数据,触发异步线程清除缓存,在清除操作没有完成之前,缓存中仍然有3个数据,且 3 个数据均可读,缓存的大小也是 3,只有当缓存操作完成了,缓存中才只剩 2 个数据,至于清除掉了哪个数据,这就要看清除策略了。

maximumWeight 最大权重

存入缓存的每个元素都要有一个权重值,当缓存中所有元素的权重值超过最大权重时,就会触发异步清除。下面给个例子。

class Student{
    
    
        Integer score;
        String name;
}
	Caffeine<String, Student> caffeine = Caffeine.newBuilder()
            .maximumWeight(100)
            .weigher((String key, Person value)-> value.getScore());
    Cache<String, Student> cache = caffeine.build();
    cache.put("one", new Student(40, "one"));
    cache.put("two", new Student(60, "two"));
    cache.put("three", new Student(50, "three"));
    Thread.sleep(10);
    System.out.println(cache.estimatedSize());
    System.out.println(cache.getIfPresent("two"));
    

运行结果:

2
null

要使用权重来衡量的话,就要规定权重是什么,每个元素的权重怎么计算,weigher 方法就是设置权重规则的,它的参数是一个函数,函数的参数是 key 和 value,函数的返回值就是元素的权重,比如上述代码中,caffeine 设置了最大权重值为 100,然后将每个 Student对象的 socre成绩作为权重值,所以整个意思就是:缓存中存储的是 Student对象,但是限制所有对象的 score总和不能超过 100,否则就触发异步清除缓存。

特别要注意一点:最大容量 和 最大权重 只能二选一作为缓存空间的限制。

CacheStats 默认的缓存状态收集器

默认情况下,缓存的状态会用一个 CacheStats 对象记录下来,通过访问 CacheStats 对象就可以知道当前缓存的各种状态指标,那究竟有哪些指标呢?
先说一下什么是“加载”,当查询缓存时,缓存未命中,那就需要去第三方数据库中查询,然后将查询出的数据先存入缓存,再返回给查询者,这个过程就是加载。

caffeine 过期策略

在Caffeine中分为两种缓存,一个是有界缓存,一个是无界缓存,无界缓存不需要过期并且没有界限。在有界缓存中提供了三个过期API:

  1. expireAfterWrite:代表着写了之后多久过期。(上面列子就是这种方式)
  2. expireAfterAccess:代表着最后一次访问了之后多久过期。
  3. expireAfter:在expireAfter中需要自己实现Expiry接口,这个接口支持create,update,以及access了之后多久过期。注意这个API和前面两个API是互斥的。这里和前面两个API不同的是,需要你告诉缓存框架,他应该在具体的某个时间过期,也就是通过前面的重写create,update,以及access的方法,获取具体的过期时间。

caffeine 更新策略

何为更新策略?就是在设定多长时间后会自动刷新缓存。

Caffeine提供了refreshAfterWrite()方法来让我们进行写后多久更新策略:

    LoadingCache<String, String> build = CacheBuilder.newBuilder().
   			 refreshAfterWrite(1, TimeUnit.DAYS).build(new CacheLoader<String, String>() {
    
    
        @OverridepublicString
        load(String key) {
    
    
            return "";
        }
    });
}

caffeine 打点监控

在Caffeine中提供了一些的打点监控策略,通过recordStats()Api进行开启,默认是使用Caffeine自带的,也可以自己进行实现。在StatsCounter接口中,定义了需要打点的方法目前来说有如下几个:

  1. recordHits:记录缓存命中
  2. recordMisses:记录缓存未命中
  3. recordLoadSuccess:记录加载成功(指的是CacheLoader加载成功)
  4. recordLoadFailure:记录加载失败
  5. recordEviction:记录淘汰数据

通过上面的监听,我们可以实时监控缓存当前的状态,以评估缓存的健康程度以及缓存命中率等,方便后续调整参数。

caffeine 淘汰监听

有很多时候我们需要知道Caffeine中的缓存为什么被淘汰了呢,从而进行一些优化?这个时候我们就需要一个监听器,代码如下所示:

Cache<String, String> cache = Caffeine.newBuilder().removaListener(((key, value, cause) -> {
    
    
    System.out.println(cause);
})).build();

在Caffeine中被淘汰的原因有很多种:

  1. EXPLICIT: 这个原因是,用户造成的,通过调用remove方法从而进行删除。
  2. REPLACED: 更新的时候,其实相当于把老的value给删了。
  3. COLLECTED: 用于我们的垃圾收集器,也就是我们上面减少的软引用,弱引用。
  4. EXPIRED:过期淘汰。
  5. SIZE: 大小淘汰,当超过最大的时候就会进行淘汰。

猜你喜欢

转载自blog.csdn.net/weixin_44427181/article/details/124817522