Guava Cache的使用

作者: JohnShen | 来源:发表于2016-01-07 16:58 被阅读8674次

本文部分内容翻译自官方文档,部分内容和示例根据自己的理解完成
原文地址:https://github.com/google/guava/wiki/CachesExplained

Guava Cache的适用场景

Cache(缓存)在很广泛的场景下都是很有用的。比如,当一个值的计算或者检索的代价很大,并且在稍后的特定输入发生后,你将不止一次的需要这个值的时候,就应当考虑使用Cache了。

Cache是和ConcurrentMap很相像的东西。最本质的不同就是,ConcurrentMap持有所有被添加进的元素,直到它们专门被移除。从另一方面来说,为了使得内存的使用可控,Cache通常来说是可配置来自动回收元素的。在一些情况下,即使LoadingCache也会很有用,虽然由于他的自动缓存加载机制,它不回收元素。

通常,Guava Cache组件在下面的场景中适用:

  • 你想花费一些内存来提高速度。
  • 你预期到一些key的值将被不止一次地被查询。
  • 你的缓存将不会需要比内存能够存储的数据更多。(Guava Cache对于一个单独运行的应用来说是本地的。它们不回将数据存储在文件中或者外部服务器上。如果这不适合你的需求,那么考虑使用其它的工具,比如Memcached

如果上面的这些都符合你的需要,那么Guava Cache组件将适合你!
获得一个Cache是使用CacheBuilder来完成的。

注意:如果你不需要Cache的一些特性,那么ConcurrentHashMap是更加有内存效率的--但是使用任何旧的ConcurrentMap都会很难或者几乎不可能去完成Cache所支持的一些特性。

Guava Cache的使用示例

使用缓存时,最常遇到的场景需要就是:

"获取缓存-如果没有-则计算"[get-if-absent-compute]的原子语义.

具体含义:

  1. 从缓存中取。
  2. 缓存中存在该数据,直接返回;
  3. 缓存中不存在该数据,从数据源中取。
  4. 数据源中存在该数据,放入缓存,并返回;
  5. 数据源中不存在该数据,返回空。

下面这段代码实现了一个做在DAO层的本地缓存。其中,getPOIList()是对外提供的方法,该方法中实现了上述逻辑。getPOIListFromDB()是用于模拟从数据库中取数据的方法。

import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class Dao {
    Cache<String, List<String>> poiCache = CacheBuilder.newBuilder().build();

    @SuppressWarnings("unchecked")
    public List<String> getPOIList(final String cityId) {
        List returnList = null;
        try {
            returnList = poiCache.get(cityId, new Callable<List<String>>() {
                @Override
                public List<String> call(){
                    return getPOIListFromDB(cityId);
                }
            });
        } catch (ExecutionException e) {
            // 记日志
        }
        return Optional.fromNullable(returnList).or(Collections.EMPTY_LIST);
    }

    @SuppressWarnings("unchecked")
    private List<String> getPOIListFromDB(String cityId){
        System.out.println("getting from DB, please wait...");
        List<String> returnList = null;
        // 模仿从数据库中取数据
        try {
            Thread.sleep(1000);
            switch (cityId){
                case "0101" : 
                        returnList = ImmutableList.of("望京", "望京西", "望京南", "望京北"); break;
                case "0102" : 
                        returnList = ImmutableList.of("a", "b", "c", "d"); break;
            }
        } catch (Exception e) {
            // 记日志
        }
        return Optional.fromNullable(returnList).or(Collections.EMPTY_LIST);
    }
}

用于测试的主方法:

import java.util.Arrays;

public class CacheTest {
    public static void main(String[] args) {
        Dao dao = new Dao();

        for (int i = 0; i < 3; i++) {
            System.out.println("--- " + i + " ---");
            System.out.println(Arrays.toString(dao.getPOIList("0101").toArray()));
            System.out.println(Arrays.toString(dao.getPOIList("0102").toArray()));
            System.out.println(Arrays.toString(dao.getPOIList("0103").toArray()));
            System.out.println();
        }
    }
}

控制台输出:

--- 0 ---
getting from DB, please wait...
[望京, 望京西, 望京南, 望京北]
getting from DB, please wait...
[a, b, c, d]
getting from DB, please wait...
[]

--- 1 ---
[望京, 望京西, 望京南, 望京北]
[a, b, c, d]
[]

--- 2 ---
[望京, 望京西, 望京南, 望京北]
[a, b, c, d]
[]

运行后的输出结果表明了,只有第一次是从DB中取的数据,之后的两次都是从cache中取得的。

为什么要把Cache对象放在DAO层?

思考一下Cache的用途,能够想到在一个Application中,对同一种对象的缓存只需要一个就够了。比如示例中的poiCache是缓存city对应的poi信息的,那么在这个Application中最好只存在一个poiCache。
实现这样的要求有以下几种方式:

  1. Spring的Singleton模式(推荐)
    比较常见的做法是将Server和DAO都作为单例的bean交给Spring容器管理,而将缓存放在DAO层不但实现了单例,也更合理(数据的缓存策略本身就应是数据访问层的一部分)。
  2. 静态初始化(不推荐)

相关文章

  • Guava Cache用法介绍

    Guava Cache的使用场景 参考1 Guava Cache用法介绍2 分布式系统缓存系列之guava cache

  • 高并发系统技术梳理

    缓存 缓存使用常见问题归纳Guava Cache系列之一Guava Cache系列之二Guava Cache系列之...

  • cache

    guava cache的使用

  • java之guava cache应用

    google的guava cache是一个轻量级进程内缓存框架。 如何使用guava cache a. 引入方便,...

  • Guava Cache

    原文 使用Guava cache构建本地缓存 - sameLuo的个人空间 - OSCHINA Guava Cac...

  • Guava Cache 使用

    缓存分为本地缓存和远端缓存。常见的远端缓存有 Redis,MongoDB;本地缓存一般使用 map 的方式保存在本...

  • Guava cache源码解析之构造缓存器

    guava cache使用介绍 guava cache是目前最长用的本地缓存,可以把它看成是增加了一些额外功能的C...

  • Guava Cache的使用

    本文部分内容翻译自官方文档,部分内容和示例根据自己的理解完成原文地址:https://github.com/goo...

  • guava 本地缓存

    介绍 Guava cache是本地缓存的一种实现。 Guava Cache与ConcurrentMap很相似,但也...

  • Guava Cache本地缓存使用小结

    原创文章,转载请注明原文章地址,谢谢! Guava Cache介绍 guava cache是google guav...

网友评论

  • 8aa991ef9860:当dao.getPOIList("0104")的时候会报错,因为不能放一个空进去
    我想请问楼主的是,当不能从缓存里获取数据,也不能从数据库里取到数据的时候,如何不执行把数据放入缓存呢。
    JohnShen:我明白你的意思,(留意“return Optional.fromNullable(returnList).or(Collections.EMPTY_LIST);”这段代码)。如果从数据库中查到的结果集对象是null,那么就把它设为了空集对象而非null值。这样缓存中对应的key=“0104”的时候,value就是[ ]。当然,这样做有个弊端就是,在缓存没有过期的时候,即使DB的结果集已经不为空了,调用时依然返回的是缓存中空集。所以应该为缓存设置一个合理的过期时间。
    其实,我在生产环境没有真正用过GuavaCache。这个是写着玩儿的,😄
  • 江流儿sai:麻烦问一下 学这个guava cache有推荐的书籍或者视屏吗?最主要想与spring整合的那种
    JohnShen:建议单纯的Cache不要用GuavaCache哈,试试更强大和专业的EhCache。举个栗子,类似于putIfAbsent这样的原子操作,GuavaCache都没有提供。

本文标题:Guava Cache的使用

本文链接:https://www.haomeiwen.com/subject/ehhzhttx.html