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 的异步刷新机制 + 多级缓存 ,取得了非常好得优化效果。
当然,我们在使用这种方式时,依然需要考虑的数据的一致性问题。
参考资料:
如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,非常感谢!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
高性能 Python 解释器 PyPy 已从 Mercurial 迁移到 Git
PyPy已将其官方仓库和问题跟踪器从 https://foss.heptapod.net/pypy/pypy 迁移到 https://github.com/pypy/pypy,也正式从 Mercurial 转移到 Git。 官方团队仍然认为Mercurial是更好的版本控制系统,命名分支模型和用户界面都优于其它选择,但foss.heptapod.net在google/bing/duckduckgo搜索中的索引并不完善,所以人们很难搜索到项目中的 issue。除此之外,还有其它一些迁移原因: 自从Heptapod加强了垃圾邮件控制后,经常会收到报告说用户创建的 issue 会被标记为垃圾邮件。 开源已经成为GitHub的代名词。 当前大部分开发都是在修复 issue,如果所有代码都在同一个平台上,那么跟踪交错 issue 就更容易了。 社区虽然提出了两个反对迁移的论点。但事实证明没有迁移到 GitHub 会阻碍贡献和报告 issue。 希望继续使用Mercurial 的人可以使用相同的方法在 GitHub上操作。 GitHub比foss.heptapod.net资源更丰富,可以添加CI作...
- 下一篇
Databend 开源周报第 126 期
Databend 是一款现代云数仓。专为弹性和高效设计,为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务:https://app.databend.cn 。 What's On In Databend 探索 Databend 本周新进展,遇到更贴近你心意的 Databend 。 全新 Filter 执行框架 在全新的 Filter 执行框架中,Databend 引入了一个开创性的概念 —— “不可变索引”。 🚀 不可变索引使我们能够在遇到 AND 和 OR 操作时避免生成临时选择缓冲区。这不仅减少了内存碎片化,还消除了从临时选择到最终选择的循环复制。 根据测试,此优化可以将原本需要 14.5s 的查询优化至 9.7s 。 如果您想了解更多信息,欢迎联系 Databend 团队,或查看下面列出的资源。 PR #13846 | feat(query): new filter execution framework) Code Corner 一起来探索 Databend 和周边生态中的代码片段或项目。 优化查询性能 Databend 通过提供聚合索引、聚类键和虚拟列,使得用户能够根...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2全家桶,快速入门学习开发网站教程