-
什么是缓存
- 程序经常要调用的对象存在内存中,方便其使用时可以快速调用,不必去数据库或者其他持久化设备中查询,主要就是提高性能
- DNS缓存、前端缓存、代理服务器缓存Nginx、应用程序缓存(本地缓存、分布式缓存)、数据库缓存
-
分布式缓存
-
与应用分离的缓存组件或服务,与本地应用隔离一个独立的应用,多个应用可直接的共享缓存
-
常见的分布式缓存 Redis、Memcached等
-
-
本地缓存
- 和业务程序一起的缓存,例如myabtis的一级或者二级缓存,本地缓存自然是最快的,但是不能在多个节点共享
- 常见的本地缓存:myabtis 一级缓存、mybatis二级缓存;框架本身的缓存; redis本地单机服务;ehchche;guava cache、Caffeine等
-
选择本地缓存和分布式缓存
- 和业务数据结合去选择
- 高并发项目里面一般都是有本地缓存和分布式缓存共同存在的
Guava Cache
以下示例未作缓存无数据操作
- github地址:https://github.com/google/guava/wiki/CachesExplained
- 全内存的本地缓存实现
- 高性能且功能丰富
- 线程安全,操作简单 (底层实现机制类似ConcurrentMap)
Step1:添加依赖
<!--guava依赖包-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
Step2:创建工具类封装api方法
/**
* Guava 本地缓存API
*/
@Component
public class BaseCache {
/**
* 分钟
*/
private Cache<String,Object> tenMinuteCache = CacheBuilder.newBuilder()
//设置缓存初始大小,应该合理设置,后续会扩容 底层也是HashMap的分段锁 无并发冲突
.initialCapacity(10)
//最大值 设置过大可能内存冲突
.maximumSize(100)
//并发数设置 同时多少个线程往里面写
.concurrencyLevel(5)
//缓存过期时间,写入后10分钟过期
.expireAfterWrite(600, TimeUnit.SECONDS)
//统计缓存命中率
.recordStats()
//构建类
.build();
/**
* 小时
*/
private Cache<String,Object> oneHourCache = CacheBuilder.newBuilder()
//设置缓存初始大小,应该合理设置,后续会扩容 底层也是HashMap的分段锁 无并发冲突
.initialCapacity(30)
//最大值 设置过大可能内存冲突
.maximumSize(100)
//并发数设置 同时多少个线程往里面写
.concurrencyLevel(5)
//缓存过期时间,写入后1小时过期
.expireAfterWrite(3600, TimeUnit.SECONDS)
//统计缓存命中率
.recordStats()
//构建类
.build();
public Cache<String, Object> getOneHourCache() {
return oneHourCache;
}
public void setOneHourCache(Cache<String, Object> oneHourCache) {
this.oneHourCache = oneHourCache;
}
public Cache<String, Object> getTenMinuteCache() {
return tenMinuteCache;
}
public void setTenMinuteCache(Cache<String, Object> tenMinuteCache) {
this.tenMinuteCache = tenMinuteCache;
}
}
Step3:实现类调取接口使用
下面的操作就是一个正常查询列表数据的操作,dao层什么的我就不往上放了
@Service
public class VideoBannerServiceImpl implements VideoBannerService {
@Autowired
private VideoBannerMapper videoBannerMapper;
@Autowired
private BaseCache baseCache;
@Override
public List<VideoBanner> listBanner() {
try {
//如果缓存找不到 去数据库里面找 回调函数 使用JDK8的写法
Object cacheObj = baseCache.getTenMinuteCache().get(CacheKeyManager.INDEX_BANNER_KEY,()->{
List<VideoBanner> videoBannerList = videoBannerMapper.listBanner();
System.out.println("从数据库里面找");
return videoBannerList;
});
//判断缓存中是否有值 未作缓存无数据操作
//走本地guave缓存
if(cacheObj instanceof List){
List<VideoBanner> videoBannerList = (List<VideoBanner>) cacheObj;
return videoBannerList;
}
}catch (Exception e){
e.printStackTrace();
}
//也可以返回兜底数据 如果是电商系统没什么意义 除非是新闻或者博客等项目还有用
return null;
}
}
关于测试就是调取两次相同查询接口,控制台查看一下mysql的日志打印,如果只出现一条,说明缓存实现