Guava Cache本地缓存

Posted ken-jl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Guava Cache本地缓存相关的知识,希望对你有一定的参考价值。

Guava介绍

Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库。

这个库是为了方便编码,并减少编码错误。

这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方法。

Guava Cache适用场景

1 消耗一些内存空间来提升速度;

2 缓存中存放的数据总量不会超出内存容量。

(Guava Cache是单个应用运行时的本地缓存,不把数据存放到文件或外部服务器(Memcached, Redis))

Guava Cache介绍

数据结构:ConcurrentHash (The returned cache is implemented as a hash table with similar performance characteristics to ConcurrentHashMap.)

主要特性(详见下面的相关链接):

    1 自动加载

    2 回收策略:

        2.1 基于容量

        2.2 基于存活时间

        2.3 基于权重

        2.4 基于引用

    3 移除监听器

    4 缓存访问统计

主要接口:CacheBuilder, LoadingCache, CacheStats

使用示例:

public class CacheProTest {
    LoadingCache<Long, Person> cache;
    private int cacheTimeoutSeconds = 10; // 10秒

    Integer counter = 1;

    @Before
    public void initialize() {
        System.out.println("初始化");

        cache = CacheBuilder.newBuilder()
                        /* 回收策略:基于容量(least-recently-used eviction when a maximum size is exceeded) */
                        .maximumSize(10) 
                        // .initialCapacity(initialCapacity)
                        
                        /* 回收策略:基于存活时间(time-based expiration of entries, measured since last access or last write) */
                        .expireAfterWrite(cacheTimeoutSeconds, TimeUnit.SECONDS)
                        // .expireAfterAccess(duration, unit)
                        // .refreshAfterWrite(duration, unit)
                        
                        /* 回收策略:基于权重 */
                        // .maximumWeight(maximumWeight)
                        // .weigher(weigher)

                        /* 回收策略:基于引用(keys automatically wrapped in weak references, values automatically wrapped in weak or soft references)*/
                        // .weakKeys()
                        // .weakValues()
                        // .softValues()
                        
                        // 设置并发数为5,即同一时间最多只能有5个线程往cache执行写入操作
                        // .concurrencyLevel(concurrencyLevel)

                        /* 缓存访问统计(accumulation of cache access statistics) */
                        .recordStats()
                        
                        /* 移除监听器(notification of evicted (or otherwise removed) entries) */
                        // .removalListener(listener)

                        .build(new CacheLoader<Long, Person>() {

                            /* 自动加载(automatic loading of entries into the cache) */
                            @Override
                            public Person load(Long id) throws Exception {
                                System.out.println("获取值, id=" + id);

                                // 调用接口获取值
                                Person p = new Person();
                                p.setId(id);
                                p.setName("name" + counter.toString());
                                counter++;

                                return p;
                            }
                        });
    }

    @Test
    public void test1() {
        try {
            /* 获值 */
            // ConcurrentMap<Long, Person> asMap = cache.asMap();

            // cache.get(key); //
            // cache.getAll(keys);

            // cache.getIfPresent(key);
            // cache.getAllPresent(keys);

            // cache.size();

            /* 存值 */
            // cache.put(key, value);
            // cache.putAll(m); // Map<? extends K, ? extends V> m

            /* 移除/删除 */
            // cache.refresh(key);
            // cache.invalidate(key);
            // cache.invalidateAll();
            // cache.invalidateAll(keys);
            // cache.cleanUp();

            /* 缓存访问统计 */
            CacheStats stats = cache.stats();
            stats.averageLoadPenalty();
            stats.evictionCount();
            stats.hitCount();
            stats.hitRate();
            stats.loadCount();
            stats.loadExceptionCount();
            stats.loadExceptionRate();
            stats.loadSuccessCount();
            stats.missCount();
            stats.missRate();
            stats.requestCount();
            stats.totalLoadTime();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Test
    public void test2() {
        try {
            Long id = 1L;
            Person person1 = cache.get(id);

            Thread.sleep(3L * 1000L);
            Person person2 = cache.get(id);

            Thread.sleep(11L * 1000L);
            Person person3 = cache.get(id);

            System.out.println(person1);
            System.out.println(person2);
            System.out.println(person3);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Person implements Serializable {
    private static final long serialVersionUID = 1L;

    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}

  

Guava Cache使用时需要关注的点

1 了解LoadingCache.refresh

正如LoadingCache.refresh(K)所声明,刷新表示为键加载新值,这个过程可以是异步的。

在刷新操作进行时,缓存仍然可以向其他线程返回旧值,而不像回收操作,读缓存的线程必须等待新值加载完成。

如果刷新过程抛出异常,缓存将保留旧值,而异常会在记录到日志后被丢弃[swallowed]。

重载CacheLoader.reload(K, V)可以扩展刷新时的行为,这个方法允许开发者在计算新值时使用旧的值。

 

2 了解 清理时机

使用CacheBuilder构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。

它会在写操作时顺带做少量的维护工作,或者偶尔在读操作时做——如果写操作实在太少的话。

因此使用LoadingCache.size() 一定要关注这个点。

 

相关链接

github 地址

易宝教程:Guava教程

CSDN:GuavaCache简介

并发编程网:[Google Guava] 3-缓存

 

以上是关于Guava Cache本地缓存的主要内容,如果未能解决你的问题,请参考以下文章

使用guava cache再本地缓存热点数据

SpringBoot加入Guava Cache实现本地缓存

Guava cache使用总结

缓存之王Caffeine Cache,性能比Guava更强,命中率更高!

Guava Cache 原理分析与最佳实践

是什么让 Spring5 放弃了使用 Guava Cache?