欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

本地缓存Guava Cache教程

程序员文章站 2022-06-19 12:02:44
...

1.背景

缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日长开发有很多场合,有一些数据量不是很大,不会经常改动,并且访问非常频繁。但是由于受限于硬盘IO的性能或者远程网络等原因获取可能非常的费时。会导致我们的程序非常缓慢,这在某些业务上是不能忍的!而缓存正是解决这类问题的神器!

缓存在很多系统和架构中都用广泛的应用,例如:

  • CPU缓存
  • 操作系统缓存
  • HTTP缓存
  • 数据库缓存
  • 静态文件缓存
  • 本地缓存
  • 分布式缓存

可以说在计算机和网络领域,缓存是无处不在的。可以这么说,只要有硬件性能不对等,涉及到网络传输的地方都会有缓存的身影。

缓存总体可分为两种 集中式缓存 和 分布式缓存

“集中式缓存"与"分布式缓存"的区别其实就在于“集中”与"非集中"的概念,其对象可能是服务器、内存条、硬盘等。

2.Guava Cache

Google的guava是个很好的项目,提供了诸如集合、缓存、并发、String工具类等等,实乃Java开发利器。这里说一下LoadingCache使用的使用。

2.1简单使用

使用Cache时,我们优先读取缓存,当缓存不存在时,则从实际的数据存储获取,如DB、磁盘、网络等,即get-if-absent-compute。guava提供了CacheLoader机制,允许我们通过设置Loader来自动完成这一过程。如:

Cache<String, User> cache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES);
user = cache.get(name, () -> {
    User value = query(key);//from databse, disk, etc.
    return value;
});

或者使用LoadingCache:

LoadingCache<String, User> cache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(
     new CacheLoader<String, User>() {
        @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<String, Optional<User>> cache = CacheBuilder.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build(
     new CacheLoader<String, Optional<User>>() {
        @Override
        public Optional<User> 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:

  1. 愿意消耗一些内存空间来提升速度。
  2. 预料到某些键会被多次查询。
  3. 缓存中存放的数据总量不会超出内存容量。

所以,可以将程序频繁用到的少量数据存储到Guava Cache中,以改善程序性能。

2.3 Guava Cache的refresh和expire刷新机制

看一下三种基于时间的清理或刷新缓存数据的方式:

expireAfterAccess: 当缓存项在指定的时间段内没有被读或写就会被回收。

**expireAfterWrite:**当缓存项在指定的时间段内没有更新就会被回收。

**refreshAfterWrite:**当缓存项上一次更新操作之后的多久会被刷新。

这三种之间的对比可以参考:Guava Cache的刷新机制