赞
踩
这种方式可以简单实现本地缓存,但是实际开发中不推荐使用,下面我们来实现一下这种方式。
首先创建一个管理缓存的类
- public class LocalCache {
-
- public static ConcurrentMap<String,String> cache = new ConcurrentHashMap<>();
-
- static {
- String name = 1 + "-" + UUID.randomUUID().toString();
- LocalCache.cache.put("1",name);
- System.out.println("id为"+1+"的数据添加到了缓存");
- }
- }
这个类中有一个静态代码块,静态代码块会在类加载时就执行,我们可以在这里完成对缓存的初始化,决定缓存内一开始就有哪些数据,另外我们还可以把这个类交给spring来管理。
- @Component
- public class LocalCache {
-
- public static ConcurrentMap<String,String> cache = new ConcurrentHashMap<>();
-
- static {
- String name = 1 + "-" + UUID.randomUUID();
- LocalCache.cache.put("1",name);
- System.out.println("id为"+1+"的数据添加到了缓存");
- }
-
- @PostConstruct
- public void init(){
- String name = 2 + "-" + UUID.randomUUID().toString();
- LocalCache.cache.put("2",name);
- System.out.println("id为"+2+"的数据添加到了缓存");
- }
- }
在把类交给spring管理后,在方法上加入@PostConstruct可以使这个方法默认执行
随后我们编写一个接口来测试缓存:
- @RestController
- @RequestMapping("/api/cache")
- public class CacheTestController {
-
- @GetMapping("/test/{id}")
- public String test(@PathVariable 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("缓存中不存在,查询数据库");
- name = id + "-" + UUID.randomUUID().toString();
- System.out.println(name);
- LocalCache.cache.put(String.valueOf(id),name);
- return name;
- }
-
- }
启动项目测试,控制台输出信息如下:
我们可以看到这两个初始化都被执行了,然后我们调用接口查询id为1与id为2的数据
再查询id为3的两次
可以看到我们先是生成了一次UUID存入ConcurrentHashmap,第二次查询时ConcurrentHashmap中存在了,直接从ConcurrentHashmap中获得数据,如此一来我们就实现了ConcurrentHashmap形式的本地缓存。
二、通过guava local cache实现
guava cache介绍
Guava是Google提供的一套Java工具包,而Guava Cache是一套非常完善的本地缓存机制(JVM缓存)。
Guava cache的设计来源于CurrentHashMap,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。
实际使用
首先导入依赖
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>23.0</version>
- </dependency>
编写配置文件,这里我们创建一个五秒钟过期时间的缓存方便测试
- 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()
- //设置缓存初始大小,应该合理设置,后续会扩容
- .initialCapacity(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;
- }
-
- }
-
下面我们对guava cache进行简单的使用,并尝试其中的命中率统计等功能
- @Autowired
- private 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> cache){
- CacheStats stats = cache.stats();
- return "缓存命中率:"+stats.hitRate()+"被清除缓存数:"+stats.evictionCount();
- }
首先访问一次id为1的数据,等5秒后再访问一次,然后立刻访问第三次
可以看到缓存未命中两次,其中缓存过期被删除了一次,随后在5秒内立刻访问缓存命中,缓存命中率,被清除缓存数均正确,测试完成
guava cache还有许多没有测试到的功能以及各种淘汰策略和机制,各位可以尝试深入了解。
Redis 是C语言开发的一个开源高性能键值对的内存数据库,可以用来做数据库、缓存、消息中间件等场景,是一种NoSQL(not-only sql,非关系型数据库)的数据库。
下面我们介绍在springboot项目中的使用
springboot中有redis的starter我们直接引用即可
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
然后在spring配置文件中加入如下设置,这里用的是properties形式的文件,host是部署redis的服务器ip,port是端口号,password是密码,如果没有设置密码不填即可
- spring.redis.host=127.0.0.1
- spring.redis.port=6379
- spring.redis.password=
同样我们编写接口测试
- @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;
- }
以上就是Java三种使用缓存的方式。
redis是缓存中使用比较普遍的方案,本文不过多赘述。问题来了,如何实现比redis更快的查询速度呢。答案是本地缓存,具体在Java中可以使用ConcurrentHashMap(HashMap是线程不安全的,本文使用ConcurrentHashMap实现本地高效和安全缓存)。但是既然是缓存,肯定还要整一套删除策略、最大空间限制、刷新策略。要不然随便用,可能会导致OuOfMemory异常。好了,废话不多说,上文列举了三种,这里着重介绍下主角
Guava Cache。思路来源:想比Redis还要快,只能在数据传输上下功夫,把不同服务器之间、甚至不同进程之间的数据传输都省略掉,直接把数据放在JVM中。像redis还涉及到连接,数据传输,因此ConcurrentMap是本地缓存一个比较好的方案。Guava Cache居于此,并拥有一套完善的删除策略、最大空间限制、刷新策略机制。
但Guava Cache是JVM层面的缓存,服务停掉或重启便消失了,在分布式环境中也有其局限性,因此,比较好的缓存方案是Guava Cache+Redis双管齐下。先查询Guava Cache,命中即返回,未命中再查redis。
PS:文章参考【java缓存、redis缓存、guava缓存】java中实现缓存的几种方式_java缓存cache-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。