1.背景
缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日长开发有很多场合,有一些数据量不是很大,不会经常改动,并且访问非常频繁。但是由于受限于硬盘IO的性能或者远程网络等原因获取可能非常的费时。会导致我们的程序非常缓慢,这在某些业务上是不能忍的!而缓存正是解决这类问题的神器!
缓存在很多系统和架构中都用广泛的应用,例如:
- CPU缓存
- 操作系统缓存
- HTTP缓存
- 数据库缓存
- 静态文件缓存
- 本地缓存
- 分布式缓存
可以说在计算机和网络领域,缓存是无处不在的。可以这么说,只要有硬件性能不对等,涉及到网络传输的地方都会有缓存的身影。
缓存总体可分为两种 集中式缓存 和 分布式缓存
“集中式缓存"与"分布式缓存"的区别其实就在于“集中”与"非集中"的概念,其对象可能是服务器、内存条、硬盘等。
2.Guava Cache
Google的guava是个很好的项目,提供了诸如集合、缓存、并发、String工具类等等,实乃Java开发利器。这里说一下LoadingCache使用的使用。
2.1简单使用
使用Cache时,我们优先读取缓存,当缓存不存在时,则从实际的数据存储获取,如DB、磁盘、网络等,即get-if-absent-compute。guava提供了CacheLoader机制,允许我们通过设置Loader来自动完成这一过程。如:
Cache cache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES);
user = cache.get(name, () -> {
User value = query(key);//from databse, disk, etc.
return value;
});
或者使用LoadingCache:
LoadingCache cache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(
new CacheLoader() {
@Override
public User load(String name) throws Exception {
User value = query(key);//from databse, disk, etc.
return value;
}
}
);
这可比自己写一个Map来缓存数据方便多了,而且还可以设置超时时间自动帮我们清理过期的数据。
不过需要注意一点的是,CacheLoader不允许返回的数据为NULL,否则会抛出异常:CacheLoader returned null for key
。所以我们需要保证查找的数据必须存在,或者抛出异常外部处理。在某些情况下,我们的数据可能确实不在,比如用户管理模块,我们在新增数据前,要查询原来是否已经存在该用户,那么这时候抛出异常也不合适,此时可以使用Optional来优化CacheLoader:
LoadingCache> cache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(
new CacheLoader>() {
@Override
public Optional load(String name) throws Exception {
User value = query(key);//from databse, disk, etc.
return Optional.ofNullable(value);
}
}
);
这样我们保证了CacheLoader返回值不为NULL,而业务数据是否存在,只需要判断Optional.ifPresent()就行了,同时Optional的其他函数在业务逻辑中也是非常有用的。
2.2 和Map比较
Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。
Guava Cache是在内存中缓存数据,相比较于数据库或redis存储,访问内存中的数据会更加高效。Guava官网介绍,下面的这几种情况可以考虑使用Guava Cache:
- 愿意消耗一些内存空间来提升速度。
- 预料到某些键会被多次查询。
- 缓存中存放的数据总量不会超出内存容量。
所以,可以将程序频繁用到的少量数据存储到Guava Cache中,以改善程序性能。
2.3 Guava Cache的refresh和expire刷新机制
看一下三种基于时间的清理或刷新缓存数据的方式:
expireAfterAccess: 当缓存项在指定的时间段内没有被读或写就会被回收。
expireAfterWrite:当缓存项在指定的时间段内没有更新就会被回收。
refreshAfterWrite:当缓存项上一次更新操作之后的多久会被刷新。
这三种之间的对比可以参考:Guava Cache的刷新机制