Guava Cache 异步刷新技巧,你值得拥有!
Guava Cache是一款非常优秀的本地缓存框架,提供简洁易用的 API 供开发者使用。
这篇文章,我们聊聊如何使用 Guava Cache 异步刷新技巧带飞系统性能 。

1 基本用法
首先,在 Java 应用中添加 maven 依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
然后编写测试用例:

LoadingCache 是本地缓存工具,支持配置加载函数,定时失效等功能。
例子中配置了基于容量进行回收策略,缓存最大容量设置为 100,配置了定时失效和刷新函数。
- 定时失效
配置 expireAfterWrite 后,缓存项在被创建或最后一次更新后的指定时间内会过期。
- 刷新函数
配置 refreshAfterWrite 设置刷新时间,当缓存项过期的同时可以重新加载新值 。
我们模拟过期执行 load 方法 / 重新加载执行 reload 方法的流程,执行结果见下图:

执行结果表明:Guava Cache 并没有后台任务线程异步的执行 load 或者 reload 方法。
expireAfterWrite允许一个线程执行 load 方法,其他线程阻塞等待 。当大量线程用相同的 key 获取缓存值时,只会有一个线程进入 load 方法,而其他线程则等待,直到缓存值被生成。这样也就避免了缓存击穿的危险。高并发场景下 ,这样还是会阻塞大量线程。
refreshAfterWrite允许一个线程执行 load 方法,其他线程返回旧的值。单个 key 并发下,使用 refreshAfterWrite ,虽然不会阻塞了,但是如果恰巧同时多个 key 同时过期,还是会给数据库造成压力。
为了提升系统性能,我们可以从如下两个方面来处理 :
- 减少过期的频率 ,也就是减少执行 load 方法的频率 ,配置 refresh < expire 。
- 采用异步刷新的策略,也就是线程异步加载数据,期间所有请求返回旧的缓存值。

2 两种方式实现异步刷新
2.1 重写 reload 方法

2.2 实现 asyncReloading 方法

不管使用哪种方案, 都需要定义单独的线程池来执行刷新任务 。
3 异步刷新 + 多级缓存
2018 年,笔者服务的一家电商公司需要进行 app 首页接口的性能优化。笔者花了大概两天的时间完成了整个方案,采取的是两级缓存模式,同时采用了 Guava 的异步刷新机制。
整体架构如下图所示:

缓存读取流程如下 :
1、业务网关刚启动时,本地缓存没有数据,读取 Redis 缓存,如果 Redis 缓存也没数据,则通过 RPC 调用导购服务读取数据,然后再将数据写入本地缓存和 Redis 中;若 Redis 缓存不为空,则将缓存数据写入本地缓存中。
2、由于步骤1已经对本地缓存预热,后续请求直接读取本地缓存,返回给用户端。
3、Guava 配置了 refresh 机制,每隔一段时间会调用自定义 LoadingCache 线程池(5个最大线程,5个核心线程)去导购服务同步数据到本地缓存和 Redis 中。
优化后,性能表现很好,平均耗时在 5ms 左右,同时大幅度的减少应用 GC 的频率。
该方案依然有瑕疵,一天晚上我们发现 app 端首页显示的数据时而相同,时而不同。
也就是说: 虽然 LoadingCache 线程一直在调用接口更新缓存信息,但是各个服务器本地缓存中的数据并非完成一致。
这说明了两个很重要的点:
1、惰性加载仍然可能造成多台机器的数据不一致;
2、LoadingCache 线程池数量配置的不太合理, 导致了任务堆积。
最终,我们的解决方案是:
1、异步刷新结合消息机制来更新缓存数据,也就是:当导购服务的配置发生变化时,通知业务网关重新拉取数据,更新缓存。
2、适当调大 LoadingCache 的线程池参数,并在线程池埋点,监控线程池的使用情况,当线程繁忙时能发出告警,然后动态修改线程池参数。
4 总结
Guava Cache 非常强大,但它并没有后台任务线程异步的执行 load 或者 reload 方法,而是通过请求线程来执行相关操作。
为了提升系统性能,我们可以从如下两个方面来处理 :
- 减少过期的频率 ,也就是减少执行 load 方法的频率 ,配置 refresh < expire 。
- 采用异步刷新的策略,也就是线程异步加载数据,期间所有请求返回旧的缓存值。
笔者曾经优化过某电商网站的首页接口,使用的方案是: Guava 的异步刷新机制 + 多级缓存 ,取得了非常好得优化效果。
当然,我们在使用这种方式时,依然需要考虑的数据的一致性问题。
参考资料:
如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

Guava Cache 异步刷新技巧,你值得拥有!的更多相关文章
- Guava Cache在实际项目中的应用
对于Guava Cache本身就不多做介绍了,一个非常好用的本地cache lib,可以完全取代自己手动维护ConcurrentHashMap. 背景 目前需要开发一个接口I,对性能要求有非常高的要求 ...
- Guava Cache,Java本地内存缓存使用实践
Guava Cache,网上介绍很多,我就不赘述了. 分享一篇好的文章: Guava Cache内存缓存使用实践-定时异步刷新及简单抽象封装 Google Guava 3-缓存 在原作者基础上,我做了 ...
- Guava Cache 原理分析与最佳实践
前言 目前大部分互联网架构 Cache 已经成为了必可不少的一环.常用的方案有大家熟知的 NoSQL 数据库(Redis.Memcached),也有大量的进程内缓存比如 EhCache .Guava ...
- 重新认识下JVM级别的本地缓存框架Guava Cache(2)——深入解读其容量限制与数据淘汰策略
大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 通过<重新认识下JVM级别的本地 ...
- Spring cache简单使用guava cache
Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...
- [Java 缓存] Java Cache之 Guava Cache的简单应用.
前言 今天第一次使用MarkDown的形式发博客. 准备记录一下自己对Guava Cache的认识及项目中的实际使用经验. 一: 什么是Guava Guava工程包含了若干被Google的 Java项 ...
- Guava学习笔记:Guava cache
缓存,在我们日常开发中是必不可少的一种解决性能问题的方法.简单的说,cache 就是为了提升系统性能而开辟的一块内存空间. 缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用 ...
- Ehcache与Guava Cache的区别浅谈
最近在做一些缓存改造的场景,有如下一些经验总结: 缓存版本: Ehcache:2.8.3 Guava:17.0 Ehcache支持持久化到本地磁盘,Guava不可以: Ehcache有现成的集群解决方 ...
- guava cache
适用场景 缓存在很多场景下都是相当有用的.例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存. Guava Cache与ConcurrentMap很相似,但 ...
- 第七章 企业项目开发--本地缓存guava cache
1.在实际项目开发中,会使用到很多缓存技术,而且数据库的设计一般也会依赖于有缓存的情况下设计. 常用的缓存分两种:本地缓存和分布式缓存. 常用的本地缓存是guava cache,本章主要介绍guava ...
随机推荐
- 【Redis】SpringBoot集成Redis事务-亲测
大家好,我是mep.今天一起来探讨一下Redis缓存的问题,SpringBoot如何集成Redis网上文章很多,基本都是介绍如何配置redisTemplate,如何调用,本文就不过多介绍了.这次我们研 ...
- CAP项目集成带身份和证书验证的MongoDB
大家好,我是Edison. 最近,在使用CAP事件总线时,碰到了这样一个需求:微服务采用的是MongoDB,而且还是带身份验证 和 SSL根证书验证的.由于目前网上能找到的资料,都是不带身份验证的Mo ...
- 利用SPI实现全自动化——LCD屏与RGB灯
如果你开启了广告屏蔽,请将博客园加入白名单,帮助博客园渡过难关,谢谢! 前言 在21年做物理实验和23年客串电赛之后,我带着STM32重回电子DIY界.这次的项目是一个电池供电的补光灯,由于用途更偏向 ...
- 调节LED的亮度
虽然Devices Plus已经介绍过Arduino的多种应用和作品示例,但是了解相关基础知识对于任何项目的构建仍然是非常重要的!这次,我们将为您介绍Arduino电子制作的基础知识.此次的分享嘉宾是 ...
- HCTF 2023 wp
HCTF 2023 wp 一.Misc 1.玩原神玩的 分析:附件为一张图片 观察最后一行,明显有flag的格式 搜索得知是 对照得flag为:hctf{yuanlainiyewanyuanshenh ...
- Vue之自定义过滤器
使用Vue.filter('过滤器名称',方法); 1. <!DOCTYPE html> <html lang="en"> <head> < ...
- js各种宽高的总结
1.clientWidth和clientHeight指元素的可视部分宽度和高度,就是padding+content如果没有滚动条,就是设定的宽度和高度 如果有滚动条,就是设定的宽度和高度减去滚动条的宽 ...
- 二进制枚举&爆搜DFS
给定一个如下图所示的全圆量角器. 初始时,量角器上的指针指向刻度 0. 现在,请你对指针进行 n 次拨动操作,每次操作给定一个拨动角度 ai,由你将指针拨动 ai 度,每次的拨动方向(顺时针或逆时针) ...
- 子组件emit 父组件方法,成功后回调执行子组件方法
场景: 父组件 update方法 子组件 确定按钮 getlist 刷新列表 子组件点击确定按钮,调用父组件新增接口,新增成功以后,子组件列表刷新 子组件: emit("confirmPa ...
- 阿里发布AI编码助手:通义灵码,兼容 VS Code、IDEA等主流编程工具
今天是阿里云栖大会的第一天,相信场外的瓜,大家都吃过了.这里就不说了,有兴趣可以看看这里:云栖大会变成相亲现场,最新招婿鄙视链来了... . 这里主要说说阿里还发布了一款AI编码助手,对于我们开发者来 ...