coding……
但行好事 莫问前程

Guava Cache

缓存在很多场景下都是相当有用的,比如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。开发中也有很多可以选择,比如EhCache、Redis等都是不错的解决方案。作为一个比较全面的开发工具包,Guava也提供了一套缓存工具,不过我看了一下,感觉其实使用起来并没有Redis方便(EhCache还没使用过)。相关文档也表示,不建议使用Guava做大规模缓存工具。所以本片文章,我只简单的做一个Guava Cache使用的例子,不做太多深入的学习,有兴趣的同学可以去深入了解一下,Guava Cache用户文档

通常来说,Guava Cache适用于:
你愿意消耗一些内存空间来提升速度。
你预料到某些键会被查询一次以上。
缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具)
如果你的场景符合上述的每一条,Guava Cache就适合你。
如同范例代码展示的一样,Cache实例通过CacheBuilder生成器模式获取,但是自定义你的缓存才是最有趣的部分。
注:如果你不需要Cache中的特性,使用ConcurrentHashMap有更好的内存效率——但Cache的大多数特性都很难基于旧有的ConcurrentMap复制,甚至根本不可能做到。

任何cache都应该提供get-if-absent-compute这一基础原子语义,具体含义如下:

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

如下例子使用Guava Cache实现一个DAO层的缓存,getList对外提供获取List列表方法并整合Guava Cache实现缓存,getListFromDb方法模拟从数据库获取数据,符合上述get-if-absent-compute原子语义。

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

    @SuppressWarnings("unchecked")
    public List<String> getList(final String cityId) {
        List returnList = null;
        try {
            returnList = poiCache.get(cityId, () -> getListFromDb(cityId));
        } catch (ExecutionException e) {
            // 记日志
        }
        return returnList;
    }

    @SuppressWarnings("unchecked")
    private List<String> getListFromDb(String cityId){
        System.out.println("getting from DB, please wait...");
        List<String> returnList = Lists.newArrayList();
        // 模拟从数据库中取数据
        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 returnList;
    }
}

测试缓存是否生效:

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(dao.getList("0101"));
            System.out.println(dao.getList("0102"));
            System.out.println(dao.getList("0103"));
            System.out.println(dao.getList("0104"));
            System.out.println();
        }
    }
}

结果输出:

Connected to the target VM, address: '127.0.0.1:5298', transport: 'socket'
--- 0 ---
getting from DB, please wait...
[北京, 上海, 广州, 深圳]
getting from DB, please wait...
[a, b, c, d]
getting from DB, please wait...
[]
getting from DB, please wait...
[]

--- 1 ---
[北京, 上海, 广州, 深圳]
[a, b, c, d]
[]
[]

--- 2 ---
[北京, 上海, 广州, 深圳]
[a, b, c, d]
[]
[]

只有第一次查询调用了getListFromDb方法,说明缓存生效了。分享一篇我感觉写的比较好的关于Guava Cache的文章,有兴趣的同学可以看一下Guava Cache内存缓存使用实践-定时异步刷新及简单抽象封装

测试代码:码云 – 卓立 – Guava测试Cache

  1. Google Guava Docs
  2. Google Guava User Guide
  3. 集中式内存缓存Guava Cache

赞(1) 打赏
Zhuoli's Blog » Guava Cache
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址