不羡鸳鸯不羡仙,一行代码调半天。原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。java
我想把记忆缓存起来,等再次见到你,就可以很快认出你。git
可以说出这么有哲理的话,得益于我对缓存的理解,以及对它的看重。没有了缓存,个人人生就没有了意义。程序员
缓存是很是重要的,工做中大部分工做能够说是和缓存打交道。因为使用普遍,因此针对缓存系统的任何优化,若是可以提升一丁点儿性能,就会让人无比兴奋。github
很长一段时间,我都在用Guava
的LoadingCache
。它和ConcurrentHashMap
是很是像的,但在其上封装了一些好用的逐出策略和并发优化,就显得好用的多。算法
今天主要说的是Caffeine
,中文名就是咖啡因,一种容易让人精神亢奋的物质。它能够说是Guava的重写,可是效率却很是的高,青出于蓝而胜于蓝。编程
下图是Caffeine的一张性能测试图。能够看到它的性能,甩了GuavaCache老远。这是为何呢? localfile://media/15938442367524/15938448827543.jpg 缓存
ConcurrentLinkedHashMap
这个类,而这个类又是GuavaCache的基础。
Ben Manes
一拍脑壳,决定更上层楼。
后浪Caffeine一来,GuavaCache就已经OUT了。微信
Caffeine支持异步加载方式,直接返回CompletableFutures
,相对于GuavaCache的同步方式,它不用阻塞等待数据的载入。另外,它的编程模型是友好的,省去了不少重复的工做。架构
GuavaCache是基于LRU的,而Caffeine是基于LRU和LFU的,结合了二者的优势。对这两个算法不太清楚的同窗,能够参考xjjdog以前的文章: 《3种堆内缓存算法,赠源码和设计思路》并发
二者合体以后,变成了新的W-TinyLFU
算法,它的命中率很是高,内存占用更加的小,这是主要缘由所在。
Caffeine另一个比较快的缘由,就是不少操做都使用了异步,把这些事件提交到队列里。队列使用的RingBuffer
,看到这个名词,我不自觉的想到了lmax
的Disruptor
,它已经成了无锁高并发的代名词。
咱们决定拿线上的数据进行验证一下。事实上,大部分比较重要的Cache,我都已经使用Caffeine替换了,完成了骚气的升级。
因为它们的API
长得很是像,这个过程是无痛的,连麻药都不须要打。
其中有个业务,有一个大的堆内缓存,缓存了用户数据。里面包含用户名、性别、地址、积分等属性,造成了一个JSON对象,但大小不超过1KB。经过灰度,根据不一样的策略,咱们测试了它的实际命中率。
策略1
1w
用户命中率:
策略2
命中率(依然是高一筹):
策略3
此时的命中率:
Caffeine的命中率一直是领先的。命中率高,效率天然也就高。调整到50%以上,咱们的缓存做用就很大了。
再放上官方的两张测试图:
(1) Read (75%) / Write (25%) localfile://media/15938442367524/15938463845793.jpg
(2) Write (100%) localfile://media/15938442367524/15938464259214.jpg
(3) Read (100%) localfile://media/15938442367524/15938464366776.jpg
咱们一直在提Caffeine的异步加载。那代码到底长什么样子呢?异步加载缓存使用了响应式编程模型,返回的是CompletableFuture
对象。说实话,代码长得和Guava很像。
public static void main(String[] args) {
AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
.maximumSize(1000)
.buildAsync(key -> slowMethod(key));
CompletableFuture<String> g = loadingCache.get("test");
String value = g.get();
}
static String slowMethod(String key) throws Exception {
Thread.sleep(1000);
return key + ".result";
}
复制代码
我记得前段时间翻Spring的源码时,也看到过它。 localfile://media/15938442367524/15938479597151.jpg
CacheManager
的Bean,便可与
Springboot-cache
进行集成,能够说是很方便了。
关键代码。
//bean生成
@Bean("caffeineCacheManager")
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder() .maximumSize(1000));
return cacheManager;
}
//使用注入
@CacheConfig(cacheNames = "caffeineCacheManager")
//信息缓存
@Cacheable(key = "#id")
复制代码
技术框架这么多,什么时候是尽头。
做者简介:小姐姐味道 (xjjdog),一个不容许程序员走弯路的公众号。聚焦基础架构和Linux。十年架构,日百亿流量,与你探讨高并发世界,给你不同的味道。个人我的微信xjjdog0,欢迎添加好友,进一步交流。 交流。