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 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 cache = new HashMap(); static { String name = UUID.randomUUID().toString(); LocalCache.cache.put(String.valueOf(1),name); System.out.println("id为1的数据添加到了缓存"); } @PostConstruct public 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 实现
Tips:Guava是 Google提供的一套Java工具包,Guava Cache是一套非常完善的本地缓存机制(JVM缓存),工具类就是封装平常常用的方法,不需要你重复造轮子,节省开发人员时间,我们一般需要知道怎么使用。其设计来源于 CurrentHashMap,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。
Guava 提供以下方面的能力
-
集合 [collections]
-
缓存 [caching]
-
原生类型支持 [primitives support]
-
并发库 [concurrency libraries]
-
通用注解 [common annotations]
-
字符串处理 [string processing]
-
I/O 等等。
Step-1:导入guava 依赖
com.google.guava guava 32.1.3-jre
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 fiveSecondCache = CacheBuilder.newBuilder() //设置缓存初始大小,应该合理设置,后续会扩容 .initalCapacity(10) //最大值 .maximumSize(100) //并发数设置 .concurrencyLevel(5) //缓存过期时间,写入后5秒钟过期 .expireAfterWrite(5,TimeUnit.SECONDS) //统计缓存命中率 .recordStats() .build() public Cache getFiveSecondCache() { return fiveSecondCache; } // 这里就是拿到缓存对象。 public void setFiveSecondCache(Cache fiveSecondCache) { this.fiveSecondCache = fiveSecondCache; } }
Step-3:使用 guava cache,并尝试统计命中率
public class test { @Autowired private GuavaLocalCache guavaLocalCache; @RequestMapping("guavaTest") public String guavaTest(Long id) { // 获取缓存 Cache 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 cahce) { CacheStats stats = cache.stats(); return "缓冲命中率:"+stats.hitRate() +" 被清除缓冲数:" + stats.evictionCount(); } }
三、使用redis实现缓存
Tips:Redis (全称: 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 依赖
org.springframework.boot spring-boot-starter-data-redis
在 SpringBoot 配置文件中加入设置,我使用的是yml 形式的文件,如果没有密码的话不填就好了
redis: # IP地址 host: XXX.XXX.XXX.XXX # 密码 password: XXXXXXXX # 端口,默认为6379 port: 6379 # 数据库索引 database: 0 # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 1 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms
Step-2:编写测试接口
public class TestRedis{ // 下面StringRedisTemplate 是一个继承自 RedisTemplate的类 @Autowired private 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 进行查看
参考文章
- 【java缓存、redis缓存、guava缓存】java中实现缓存的几种方式_java缓存cache-CSDN博客
- 一篇文章搞定 Redis 基础知识 - 知乎 (zhihu.com)
- Java本地缓存技术选型(Guava Cache、Caffeine、Encache) - 掘金 (juejin.cn)
- MemCache原理超详细解读(仅学习) - 知乎 (zhihu.com)
- PostConstruct注解详细使用说明及理解-CSDN博客
- PostConstruct (Java Platform SE 8 ) (oracle.com)
- Java开发利器Guava Cache之使用篇 - 掘金 (juejin.cn)
- Google guava 工具类的介绍和使用 - 掘金 (juejin.cn)
- Redis详细介绍(精简版)_redis 服务 精简-CSDN博客
- 初识Redis,看这一篇就够了
-
-