当前位置: 首页 > news >正文

Java 实现缓存的三种方式

Java 实现缓存的三种方式

文章目录

  • Java 实现缓存的三种方式
    • 一、`HashMap`实现缓存
      • `Step-1`:实现一个缓存管理类
      • `Step-2`:将缓存管理类交给 `Spring` 进行管理
      • `Step-3`:编写接口测试缓存
      • `Step-4`:结果展示
    • 二、`guava local cache` 实现
      • `Step-1`:导入`guava` 依赖
      • `Step-2`:使用`guava`创建简单缓存管理类
      • `Step-3`:使用 `guava cache`,并尝试统计命中率
    • 三、使用`redis`实现缓存
      • `Step-1`:导入`Redis` 依赖
      • `Step-2`:编写测试接口
      • `Step-3`:进行接口测试,并使用`Redis DeskTop Manager` 进行查看
    • 参考文章

一、HashMap实现缓存

​ 可以实现简单的本地缓存,但是实际开发中不推荐,我们可以简单模拟一下缓存的实现,

Step-1:实现一个缓存管理类

public class LocalCache {public static HashMap<String,String> cache = new HashMap<>();static {String name = UUID.randomUUID().toString();LocalCache.cache.put(String.valueOf(1),name);System.out.println("id为1的数据添加到了缓存");}
}
// 类中有 `static` 修饰的静态代码块,当类被加载的时候就会执行,如有不懂的可以如下博客
// https://blog.csdn.net/weixin_62636014/article/details/136851287

Tips:我们在static中完成了对缓存的初始化,你可以往缓存里面放入初始数据。

Step-2:将缓存管理类交给 Spring 进行管理

@Component
public class LocalCache {public static HashMap<String,String> cache = new HashMap<>();static {String name = UUID.randomUUID().toString();LocalCache.cache.put(String.valueOf(1),name);System.out.println("id为1的数据添加到了缓存");}@PostConstructpublic void init() {String name = UUID.randomUUID().toString();LocalCache.cache.put(String.valueOf(2),name);System.out.println("id为2的数据添加到了缓存");}
}

Tips:在将缓存管理类交给了 Spring进行管理后,在方法上加入@PostConstruct,可以使方法默认执行,注意该注解不是 Spring 框架提供,仅仅是由 Java JDK 提供的,主要是作用于 Servlet生命周期的注解,实现的是在 Bean 初始化之前自定义操作

@PostConstruct 方法在 Bean初始化中的执行顺序

  • Constructor(构造方法)

  • @Autowired(依赖注入)

  • @PostConstruct (注释的初始化方法)

Step-3:编写接口测试缓存

@RequestMapping("test")
public String test(Long id) {String name = LocalCache.cache.get(String.valueOf(id));if (name != null) {System.out.println("缓存中存在,查询缓存");System.out.println(name);return name;}System.out.println("缓存中不存在,查询数据库");// 查询数据库操作后,queryDataName方法没有写了;// 大家可以自己配一下Mybatis和JDBC进行数据库查询,达到效果是从库中查出来 name;name = queryDataName(id);System.out.println(name);LocalCache.cache.put(String.valueOf(id),name);return name;
}public String queryDataName(Long id) {String name = UUID.randomUUID().toString();return name;
}

Step-4:结果展示

​ 这个是控制台输出,每个人的随机 UUID 不一致,我这个只是一个样例

id为1的数据添加到了缓存
id为2的数据添加到了缓存
缓存中存在,查询缓存
e2eadabe-3c42-4732-b465-e085ea5faf96
缓存中不存在,查询数据库
942ffe92-454f-4046-87e5-53e8b951d2a1

二、guava local cache 实现

TipsGuavaGoogle提供的一套Java工具包,Guava Cache是一套非常完善的本地缓存机制(JVM缓存),工具类就是封装平常常用的方法,不需要你重复造轮子,节省开发人员时间,我们一般需要知道怎么使用。其设计来源于 CurrentHashMap,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。

Guava 提供以下方面的能力

  • 集合 [collections]

  • 缓存 [caching]

  • 原生类型支持 [primitives support]

  • 并发库 [concurrency libraries]

  • 通用注解 [common annotations]

  • 字符串处理 [string processing]

  • I/O 等等。

Step-1:导入guava 依赖

		<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.1.3-jre</version></dependency>

Step-2:使用guava创建简单缓存管理类

为了方便展示,这里面使用了5 秒的缓存保留时间。

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component
public class GuavaLocalCache{private Cache<String,String> fiveSecondCache = CacheBuilder.newBuilder()//设置缓存初始大小,应该合理设置,后续会扩容.initalCapacity(10)//最大值.maximumSize(100)//并发数设置.concurrencyLevel(5)//缓存过期时间,写入后5秒钟过期.expireAfterWrite(5,TimeUnit.SECONDS)//统计缓存命中率.recordStats().build()public Cache<String,String> getFiveSecondCache() {return fiveSecondCache;} // 这里就是拿到缓存对象。public void setFiveSecondCache(Cache<String,String> fiveSecondCache) {this.fiveSecondCache = fiveSecondCache;}
}

Step-3:使用 guava cache,并尝试统计命中率

public class test {@Autowiredprivate GuavaLocalCache guavaLocalCache;@RequestMapping("guavaTest")public String guavaTest(Long id) {// 获取缓存Cache<String,String> fiveSecondCache = guavaLocalCache.getFiveSecondCache();// 从缓存中获取对象String nameCache = fiveSecondCache.getIfPresent(String.valueOf(id));// 缓存中存在if (nameCache != null) {System.out.println("缓存命中:"+ nameCache + "," + getCacheStats(fiveSecondCache));return nameCache;}// 将数据存入缓存System.out.println("缓存未命中," + getCacheStats(fiveSecondCache));nameCache = id + "-" + UUID.randomUUID().toString();fiveSecondCache.put(String.valueOf(id),nameCache);return nameCache;}public String getCacheStats(Cache<String,String> cahce) {CacheStats stats = cache.stats();return "缓冲命中率:"+stats.hitRate() +" 被清除缓冲数:" + stats.evictionCount();}}

三、使用redis实现缓存

TipsRedis (全称: Remote Dictionary Server 远程字典服务)是一个开源的使用 ANSI C语言 编写、支持网络、可基于内存亦可持久化的日志型、 Key-Value数据库 。Redis 一般被用来做缓存用的,它实际上也是一种数据库(非关系型数据库),可以对经常使用到的数据进行存储,也就是大家所说的缓存。官方给出的数据是, Redis 能达到 10w+QPS( 每秒查询速度 ) 。

Tips: 为什么 Redis 的速度比 Mysql 等这种数据快呢?

​ 因为 Redis 存储的是 key-values 格式的数据,时间复杂度是 O(1) ,即直接通过 key 查询对应的 value 。 而如 Mysql 数据库,底层的实现是 B+ 树,时间复杂度是 O(logn)

​ 最重要的一点是,数据库的数据是存储在磁盘中的,而 Redis 是存储在内存当中的,它们的速度差距不言而喻。但 Redis 也支持持久化存储

Step-1:导入Redis 依赖

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

SpringBoot 配置文件中加入设置,我使用的是yml 形式的文件,如果没有密码的话不填就好了

  redis:# IP地址host: XXX.XXX.XXX.XXX# 密码password: XXXXXXXX# 端口,默认为6379port: 6379# 数据库索引database: 0# 连接超时时间timeout: 10slettuce:pool:# 连接池中的最小空闲连接min-idle: 1# 连接池中的最大空闲连接max-idle: 8# 连接池的最大数据库连接数max-active: 8# #连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms

Step-2:编写测试接口

public class TestRedis{// 下面StringRedisTemplate 是一个继承自 RedisTemplate的类@Autowiredprivate StringRedisTemplate stringRedisTemplate;@RequestMapping("/redisTest")public String redisCacheTest(Long id){String name = stringRedisTemplate.opsForValue().get(String.valueOf(id));if (name != null){System.out.println("缓存中存在,查询缓存");System.out.println(name);return name;}System.out.println("缓存中不存在,查询数据库");name = id + "-" + UUID.randomUUID().toString();System.out.println(name);stringRedisTemplate.opsForValue().set(String.valueOf(id),name);return name;}
}

Step-3:进行接口测试,并使用Redis DeskTop Manager 进行查看

参考文章

  1. 【java缓存、redis缓存、guava缓存】java中实现缓存的几种方式_java缓存cache-CSDN博客
  2. 一篇文章搞定 Redis 基础知识 - 知乎 (zhihu.com)
  3. Java本地缓存技术选型(Guava Cache、Caffeine、Encache) - 掘金 (juejin.cn)
  4. MemCache原理超详细解读(仅学习) - 知乎 (zhihu.com)
  5. PostConstruct注解详细使用说明及理解-CSDN博客
  6. PostConstruct (Java Platform SE 8 ) (oracle.com)
  7. Java开发利器Guava Cache之使用篇 - 掘金 (juejin.cn)
  8. Google guava 工具类的介绍和使用 - 掘金 (juejin.cn)
  9. Redis详细介绍(精简版)_redis 服务 精简-CSDN博客
  10. 初识Redis,看这一篇就够了

相关文章:

  • [力扣DP]72. 编辑距离
  • 机器学习——元学习
  • python外网下载指定库导入内网的方法
  • 美易官方:盘前道指期货涨0.5%,游戏驿站跌逾15%
  • Thingworx高可用集群部署(八)-Ignite集群部署
  • jsp指令和动作
  • Unity PS5开发 天坑篇 之 URP管线与HDRP管线部署流程以及出包介绍04
  • 快速幂算法在Java中的应用
  • vue页面实现左右div宽度,上下div高度分割线手动拖动高度或者宽度自动变化,两个div宽度或者高度拉伸调节,实现左右可拖动改变宽度的div内容显示区
  • 通过Caliper进行压力测试程序,且汇总压力测试问题解决
  • 20款Python办公自动化库精选,一键提升效率!
  • itextPdf生成pdf简单示例
  • 前后端实时数据通信
  • ESP32
  • python爬虫----python列表高级
  • CAP 一致性协议及应用解析
  • CSS 专业技巧
  • JavaScript设计模式之工厂模式
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • js正则,这点儿就够用了
  • Linux链接文件
  • MYSQL 的 IF 函数
  • node-glob通配符
  • python docx文档转html页面
  • python学习笔记-类对象的信息
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Vue组件定义
  • 聊聊directory traversal attack
  • 深入浏览器事件循环的本质
  • 使用putty远程连接linux
  • 树莓派 - 使用须知
  • 微信小程序开发问题汇总
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 详解移动APP与web APP的区别
  • 字符串匹配基础上
  • PostgreSQL之连接数修改
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • (三)Honghu Cloud云架构一定时调度平台
  • (十六)串口UART
  • (四) 虚拟摄像头vivi体验
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • ***检测工具之RKHunter AIDE
  • *2 echo、printf、mkdir命令的应用
  • ./configure,make,make install的作用(转)
  • .NET Framework杂记
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .NET 使用 XPath 来读写 XML 文件
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • @在php中起什么作用?
  • [.net] 如何在mail的加入正文显示图片
  • [④ADRV902x]: Digital Filter Configuration(发射端)
  • [AI]ChatGPT4 与 ChatGPT3.5 区别有多大
  • [Android Pro] AndroidX重构和映射
  • [Android]通过PhoneLookup读取所有电话号码
  • [ASP.NET 控件实作 Day7] 设定工具箱的控件图标