Caffeine史上最快的内存缓存

引言

在现代的Web应用程序中,缓存是提升性能,减少数据库负载,加快响应速度的关键技术之一。Spring Boot作为一个简化Spring应用开发的框架,提供了与多种缓存技术集成的支持。Caffeine是一个高性能,灵活的缓存库,它可以作为本地缓存在Java应用中广泛使用。本文将详细介绍如何在Spring Boot项目中集成Caffeine缓存,并通过一个实例来展示它的使用。

什么是Caffeine缓存?

Caffeine是一个Java 8的缓存库,设计目的是超越Guava缓存库的性能,提供近乎最优的命中率。它提供了一个构建高性能缓存的完整工具集,支持多种缓存策略,如:LRU(最近最少使用)、LFU(最不经常使用)和W-TinyLFU(窗口缓存策略)等。

一、本地缓存介绍

缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。

之前介绍过 Redis 这种 NoSql 作为缓存组件,它能够很好的作为分布式缓存组件提供多个服务间的缓存,但是 Redis 这种还是需要网络开销,增加时耗。本地缓存是直接从本地内存中读取,没有网络开销,例如秒杀系统或者数据量小的缓存等,比远程缓存更合适。

二、缓存组件 Caffeine 介绍

按 Caffeine Github 文档描述,Caffeine 是基于 JAVA 8 的高性能缓存库。并且在 spring5 (springboot 2.x) 后,spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。

1、Caffeine 性能

可以通过下图观测到,在下面缓存组件中 Caffeine 性能是其中最好的。
在这里插入图片描述

2、Caffeine 配置说明

参数类型描述
initialCapacityinteger初始的缓存空间大小
maximumSizelong缓存的最大条数
maximumWeightlong缓存的最大权重
expireAfterAccessduration最后一次写入或访问后,指定经过多长的时间过期
expireAfterWriteduration最后一次写入后,指定经过多长的时间缓存过期
refreshAfterWriteduration创建缓存或者最近一次更新缓存后,经过指定的时间间隔后刷新缓存
weakKeysboolean打开 key 的弱引用
weakValuesboolean打开 value 的弱引用
softValuesboolean打开 value 的软引用
recordStats-开发统计功能

注意:

  • weakValuessoftValues 不可以同时使用。
  • maximumSizemaximumWeight 不可以同时使用。
  • expireAfterWriteexpireAfterAccess 同时存在时,以 expireAfterWrite 为准。

3、软引用与弱引用

  • 软引用: 如果一个对象只具有软引用,且内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
  • 弱引用: 弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
// 软引用
Caffeine.newBuilder().softValues().build();// 弱引用
Caffeine.newBuilder().weakKeys().weakValues().build();

三、SpringBoot 集成 Caffeine 两种方式

SpringBoot 有俩种使用 Caffeine 作为缓存的方式:

  • 方式一: 直接引入 Caffeine 依赖,然后使用 Caffeine 方法实现缓存。
  • 方式二: 引入 Caffeine 和 SpringCache 依赖,使用 SpringCache 注解方法实现缓存。

下面将介绍下,这俩中集成方式都是如何实现的。

四、SpringBoot 集成 Caffeine 方式一

1、Maven 引入相关依赖

 <dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId></dependency>

2、配置缓存配置类

@Configuration
public class CacheConfig {@Beanpublic Cache<String, Object> caffeineCache() {return Caffeine.newBuilder()// 设置最后一次写入或访问后经过固定时间过期.expireAfterWrite(60, TimeUnit.SECONDS)// 初始的缓存空间大小.initialCapacity(100)// 缓存的最大条数.maximumSize(1000).build();}}

3、定义测试的实体对象

@Data
@ToString
public class UserInfo {private Integer id;private String name;private String sex;private Integer age;
}

4、定义服务接口类和实现类

服务接口

public interface UserInfoService {/*** 增加用户信息** @param userInfo 用户信息*/void addUserInfo(UserInfo userInfo);/*** 获取用户信息** @param id 用户ID* @return 用户信息*/UserInfo getByName(Integer id);/*** 修改用户信息** @param userInfo 用户信息* @return 用户信息*/UserInfo updateUserInfo(UserInfo userInfo);/*** 删除用户信息** @param id 用户ID*/void deleteById(Integer id);}

服务实现类

@Slf4j
@Service
public class UserInfoServiceImpl implements UserInfoService {/*** 模拟数据库存储数据*/private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();@AutowiredCache<String, Object> caffeineCache;@Overridepublic void addUserInfo(UserInfo userInfo) {log.info("create");userInfoMap.put(userInfo.getId(), userInfo);// 加入缓存caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);}@Overridepublic UserInfo getByName(Integer id) {// 先从缓存读取UserInfo userInfo = (UserInfo) caffeineCache.asMap().get(String.valueOf(id));if (userInfo != null){return userInfo;}// 如果缓存中不存在,则从库中查找log.info("get");userInfo = userInfoMap.get(id);// 如果用户信息不为空,则加入缓存if (userInfo != null){caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);}return userInfo;}@Overridepublic UserInfo updateUserInfo(UserInfo userInfo) {log.info("update");if (!userInfoMap.containsKey(userInfo.getId())) {return null;}// 取旧的值UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());// 替换内容if (!StringUtils.isEmpty(oldUserInfo.getAge())) {oldUserInfo.setAge(userInfo.getAge());}if (!StringUtils.isEmpty(oldUserInfo.getName())) {oldUserInfo.setName(userInfo.getName());}if (!StringUtils.isEmpty(oldUserInfo.getSex())) {oldUserInfo.setSex(userInfo.getSex());}// 将新的对象存储,更新旧对象信息userInfoMap.put(oldUserInfo.getId(), oldUserInfo);// 替换缓存中的值caffeineCache.put(String.valueOf(oldUserInfo.getId()),oldUserInfo);return oldUserInfo;}@Overridepublic void deleteById(Integer id) {log.info("delete");userInfoMap.remove(id);// 从缓存中删除caffeineCache.asMap().remove(String.valueOf(id));}}

5、测试的 Controller 类

@RestController
@RequestMapping
public class UserInfoController {@Autowiredprivate UserInfoService userInfoService;@GetMapping("/userInfo/{id}")public Object getUserInfo(@PathVariable Integer id) {UserInfo userInfo = userInfoService.getByName(id);if (userInfo == null) {return "没有该用户";}return userInfo;}@PostMapping("/userInfo")public Object createUserInfo(@RequestBody UserInfo userInfo) {userInfoService.addUserInfo(userInfo);return "SUCCESS";}@PutMapping("/userInfo")public Object updateUserInfo(@RequestBody UserInfo userInfo) {UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);if (newUserInfo == null){return "不存在该用户";}return newUserInfo;}@DeleteMapping("/userInfo/{id}")public Object deleteUserInfo(@PathVariable Integer id) {userInfoService.deleteById(id);return "SUCCESS";}}

五、SpringBoot 集成 Caffeine 方式二

1、Maven 引入相关依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId>
</dependency>

2、配置缓存配置类

@Configuration
public class CacheConfig {/*** 配置缓存管理器** @return 缓存管理器*/@Bean("caffeineCacheManager")public CacheManager cacheManager() {CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder()// 设置最后一次写入或访问后经过固定时间过期.expireAfterAccess(60, TimeUnit.SECONDS)// 初始的缓存空间大小.initialCapacity(100)// 缓存的最大条数.maximumSize(1000));return cacheManager;}}

3、定义测试的实体对象

@Data
@ToString
public class UserInfo {private Integer id;private String name;private String sex;private Integer age;
}

4、定义服务接口类和实现类

服务接口

public interface UserInfoService {/*** 增加用户信息** @param userInfo 用户信息*/void addUserInfo(UserInfo userInfo);/*** 获取用户信息** @param id 用户ID* @return 用户信息*/UserInfo getByName(Integer id);/*** 修改用户信息** @param userInfo 用户信息* @return 用户信息*/UserInfo updateUserInfo(UserInfo userInfo);/*** 删除用户信息** @param id 用户ID*/void deleteById(Integer id);}

服务实现类

@Slf4j
@Service
@CacheConfig(cacheNames = "caffeineCacheManager")
public class UserInfoServiceImpl implements UserInfoService {/*** 模拟数据库存储数据*/private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();@Override@CachePut(key = "#userInfo.id")public void addUserInfo(UserInfo userInfo) {log.info("create");userInfoMap.put(userInfo.getId(), userInfo);}@Override@Cacheable(key = "#id")public UserInfo getByName(Integer id) {log.info("get");return userInfoMap.get(id);}@Override@CachePut(key = "#userInfo.id")public UserInfo updateUserInfo(UserInfo userInfo) {log.info("update");if (!userInfoMap.containsKey(userInfo.getId())) {return null;}// 取旧的值UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());// 替换内容if (!StringUtils.isEmpty(oldUserInfo.getAge())) {oldUserInfo.setAge(userInfo.getAge());}if (!StringUtils.isEmpty(oldUserInfo.getName())) {oldUserInfo.setName(userInfo.getName());}if (!StringUtils.isEmpty(oldUserInfo.getSex())) {oldUserInfo.setSex(userInfo.getSex());}// 将新的对象存储,更新旧对象信息userInfoMap.put(oldUserInfo.getId(), oldUserInfo);// 返回新对象信息return oldUserInfo;}@Override@CacheEvict(key = "#id")public void deleteById(Integer id) {log.info("delete");userInfoMap.remove(id);}}

5、测试的 Controller 类

RestController
@RequestMapping
public class UserInfoController {@Autowiredprivate UserInfoService userInfoService;@GetMapping("/userInfo/{id}")public Object getUserInfo(@PathVariable Integer id) {UserInfo userInfo = userInfoService.getByName(id);if (userInfo == null) {return "没有该用户";}return userInfo;}@PostMapping("/userInfo")public Object createUserInfo(@RequestBody UserInfo userInfo) {userInfoService.addUserInfo(userInfo);return "SUCCESS";}@PutMapping("/userInfo")public Object updateUserInfo(@RequestBody UserInfo userInfo) {UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);if (newUserInfo == null){return "不存在该用户";}return newUserInfo;}@DeleteMapping("/userInfo/{id}")public Object deleteUserInfo(@PathVariable Integer id) {userInfoService.deleteById(id);return "SUCCESS";}}

6、创建启动类开启缓存

在启动类中加上 @EnableCaching 注解开启缓存。

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

结语

通过本文的介绍,我们了解了如何在Spring Boot应用中集成Caffeine本地缓存技术。Caffeine的高性能和易用性使其成为Java本地缓存的优秀选择。希望本文能帮助你在实际项目中有效地使用Caffeine缓存,提升应用性能。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/452257.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Transformer 自然语言处理(三)

原文&#xff1a;Natural Language Processing with Transformers 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第八章&#xff1a;使 transformers 在生产中更高效 在之前的章节中&#xff0c;您已经看到了 transformers 如何被微调以在各种任务上产生出色的结果。…

C++STL之容器

STL的概述 STL(Standard Template Library,标准模板库) STL的6大组件&#xff1a;容器、算法、迭代器、适配器、仿函数、空间配置 容器&#xff1a;存放数据 算法&#xff1a;操作数据 迭代器&#xff1a;算法 通过迭代器 操作 容器 适配器&#xff1a;为算法 提供更多的接口 …

SQL报错注入

SQL注入报错注入 报错注入原理:报错注入是通过特殊函数错误使用并使其输出错误结果来获取信息的在遇到有报错回显的时候&#xff0c;但是没有数据回显的情况下可以利用报错注入的函数: 1.floor():向下取整 2.extractvalue(): 对XML文档进行查询的函数&#xff0c;当参数的格式…

202416读书笔记|《总有人会拥抱满身带刺的你》——今天我请客,想请你快乐

202416读书笔记|《总有人会拥抱满身带刺的你》——今天我请客&#xff0c;想请你快乐 这是一篇暖萌轻松的绘本推荐记录书评&#xff0c;《总有人会拥抱满身带刺的你》纳米著&#xff0c;《今天我请客&#xff0c;想请你快乐》燕七著&#xff0c;都还不错&#xff0c;截取摘录了…

C++-模板基础

1. 泛型编程 大家在学习过程中一定写过swap函数吧&#xff0c;那么swap函数的可以写成很多种形式&#xff0c;因为形参的类型可以是任意类型&#xff0c;那么我们如果想用多种swap函数的话&#xff0c;就意味着我们必须写多个swap函数吗&#xff1f;不是的&#xff0c;C为了解…

Web项目利用OSS进行图像存储服务

一、OSS介绍 在Web项目中&#xff0c;一些常见的功能&#xff0c;比如展示图片&#xff0c;修改头像等&#xff0c;都需要进行图片的上传操作&#xff0c;但是如果是存储在Web服务器中&#xff0c;在读取图片的时候会占用比较多的资源&#xff0c;影响服务器的性能。 常…

Node需要了解的知识

Node能执行javascript的原因。 浏览器之所以能执行Javascript代码&#xff0c;因为内部含有v8引擎。Node.js基于v8引擎封装&#xff0c;因此可以执行javascript代码。Node.js环境没有DOM和BOM。DOM能访问HTML所有的节点对象&#xff0c;BOM是浏览器对象。但是node中提供了cons…

音箱、功放播放HDMI音频解决方案之HDMI音频分离器HHA

HDMI音频分离器HHA简介 HDMI音频分离器HHA具有一路HDMI信号输入&#xff0c;转换成一路HDMI信号、一路5.1光纤音频信号、一路5.1 SPDIF/同轴音频信号和一路模拟左右声道立体声信号输出&#xff0c;同时还支持EDID存储及兼容HDCP功能&#xff1b;分辨率最高支持1920*1080p&#…

Ainx框架实现 一

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于Ainx系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列…

QGraphicsScene中显示GIF动图,GIF图片在场景中移动

文章目录 效果图引言显示GIF图片&#xff08;方法一&#xff09;显示GIF图片&#xff08;方法二&#xff09;GIF图片在场景中运动 效果图 引言 当我们想在QGraphicsScene显示或者说绘制一张GIF动图时&#xff0c;该如何处理&#xff1f;Qt中的 QGraphicsItem 的 paint 函数中&…

【开源】WordPress一键崩溃宕机插件(整活娱乐)

插件介绍 可一键实现Wordpress崩溃宕机的整活向插件&#xff08;请勿用于非法途径&#xff0c;仅供整活娱乐&#xff09;。鼓励关注网站性能的提升&#xff0c;以提供更好的用户体验&#xff0c;提倡为用户提供良好体验和高效速度的原则。 介绍 长期以来&#xff0c;人们都在…

暗黑童话《潘神的迷宫》赏析

故事背景 《潘神的迷宫》是一部由吉尔莫德尔托罗执导的墨西哥电影&#xff0c;讲述了一个结合了幻想、战争、童话和现实主义元素的故事。影片发生在西班牙内战期间&#xff0c;主要通过一名名叫奥菲莉亚的女孩的视角来展开。 故事梗概&#xff1a; 奥菲莉亚和她怀孕的母亲卡…