A comparison of local caches (1) 【本地缓存之比较 (1)】

1. Spring local cache   【Spring 本地缓存】

Spring provided cacheable annotation since 3.1. It‘s very super convinient to use and can obviously boost application performance.

从3.1版本开始,Spring提供了cacheable注解。它使用起来非常方便,还可以很明显的提升应用性能。具体的,怎么使用呢?

First, create a cache bean.

首先,创建缓存bean。这里,我们设置一个三秒的本地缓存(写后3秒过期)。

@Beanpublic Cache ephemeralCache() {    return new ConcurrentMapCache(EPHEMERAL_CACHE, CacheBuilder.newBuilder()            .expireAfterWrite(3, TimeUnit.SECONDS)            .build().asMap(), false);}

Second, add @Cacheable to the existed method.

接下来,给想要使用缓存的已有方法加上@Cacheable注解

@Cacheable(cacheNames = AppCacheConfig.EPHEMERAL_CACHE, key = "{#root.methodName, ‘test‘}")public Integer genId() {    int i = ai.incrementAndGet();    System.out.println(String.format("populate cache %s", LocalDateTime.now()));    return i;}

Finally, enjoy!

然后,尽情体验缓存带来的快乐!

ExecutorService exe = Executors.newWorkStealingPool();exe.submit(() -> {    while (true) {        cacheApi.genId();        Thread.sleep(50);    }});

See output of below codes

下面的日志和最初的缓存设定完全吻合

2017-06-08 10:17:49.990  INFO 12460 --- [           main] com.loops.nbs.Application                : Started Application in 7.42 seconds (JVM running for 7.973)
populate cache 2017-06-08T10:17:52.372
populate cache 2017-06-08T10:17:55.379
populate cache 2017-06-08T10:17:58.387
populate cache 2017-06-08T10:18:01.394
populate cache 2017-06-08T10:18:04.402
populate cache 2017-06-08T10:18:07.409
populate cache 2017-06-08T10:18:10.417
populate cache 2017-06-08T10:18:13.426

2. Guava cache  【Guava 本地缓存】

Guava is a set of libraries provided by Google. In memory cache is part of it.

Guava是Google提供的一套工具库。这里只讨论其中的缓存部分。

public class GuavaCacheExample {
    static Cache<Integer, Integer> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(2, TimeUnit.SECONDS)
            .recordStats()
            .build();

    static LoadingCache<Integer, Integer> loadingCache = CacheBuilder.newBuilder()
            .expireAfterWrite(2, TimeUnit.SECONDS)
            .build(new CacheLoader<Integer, Integer>() {
                @Override
                public Integer load(Integer key) throws Exception {
                    System.out.println("populate cache");
                    return key * 10;
                }
            });

    static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public static void main(String[] args) throws Exception {
        useNormalCache();
    }

    static void useNormalCache() throws Exception {
        scheduler.scheduleWithFixedDelay(() -> System.out.println(cache.stats()), 0, 1, TimeUnit.SECONDS);
        for (int i = 0; i < 10; i++) {
            System.out.println(cache.get(0, () -> {
                Thread.sleep(500);
                return 10;
            }));
            Thread.sleep(300);
        }
    }

    static void useLoadingCache() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(loadingCache.get(1));
            Thread.sleep(300);
        }
    }
}

Usually, there are 2 types of caches

1) Cache

Cache is basic usage, it provides method to get and put cache.

Cache是基础用法,提供了get和put的操作。

2) LoadingCache

Loading cache requires a CacheLoader when cache is created. Every time when cache is expired or nonexisted, the load method will be called automatically and generate cache.

So user doesn‘t have to manually put cache back after cache is expired.

Loading cache在创建的时候需要给定 CacheLoader。如果缓存过期或者不存在,CacheLoader 的 load 方法就会被调用到并产生缓存。这样,用户就不用去关心那些过期的缓存项了。

Besides, we can add recordStats() when creating a cache object. Later we can monitor the cache usage by calling cache.stats()

Cache is created twice during the test, correspondingly, totalLoadTime (mesured in nano seconds) changed twice.

除此,我们还可以在创建缓存时使用 recordStats 来记录缓存的使用情况。之后用 cache 的 stats() 方法来观察。

测试过程中,我们的缓存产生了2次,totalLoadTime (纳秒为单位)也很好的佐证了这一点。

CacheStats{hitCount=0, missCount=0, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
10
10
CacheStats{hitCount=1, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=502324777, evictionCount=0}
10
10
10
CacheStats{hitCount=4, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=502324777, evictionCount=0}
10
10
CacheStats{hitCount=6, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=502324777, evictionCount=1}
10
10
10
CacheStats{hitCount=8, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=1002802169, evictionCount=1}

3. Caffeine cache  【Caffeine 本地缓存】

Caffeine cache is an optimized cache for Java 8. It has similar apis to guava but provide higher performance, which makes it easy to migrate from Gauva to Caffeine.

See this report for more details https://github.com/ben-manes/caffeine/wiki/Benchmarks

咖啡因缓存是专为Java 8而生的。使用上和Guava很像(很容易迁移),但它在多线程情况下性能更高。上面有一个跑分链接,比较了各种本地缓存的性能。

public class CaffeineCacheExample {
    static Cache<Integer, Integer> cache = Caffeine.newBuilder()
            .expireAfterWrite(2, TimeUnit.SECONDS)
            .recordStats()
            .build();

    static LoadingCache<Integer, Integer> loadingCache = Caffeine.newBuilder()
            .expireAfterWrite(2, TimeUnit.SECONDS)
            .build(new CacheLoader<Integer, Integer>() {
                @Override
                public Integer load(Integer key) throws Exception {
                    System.out.println("populate cache");
                    return key * 10;
                }
            });

    static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    public static void main(String[] args) throws Exception {
        useNormalCache();
    }

    static void useNormalCache() throws Exception {
        scheduler.scheduleWithFixedDelay(() -> System.out.println(cache.stats()), 0, 1, TimeUnit.SECONDS);
        for (int i = 0; i < 10; i++) {
            System.out.println(cache.get(0, k -> {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) {
                    //ignore
                }
                return 10;
            }));
            Thread.sleep(300);
        }
    }

    static void useLoadingCache() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(loadingCache.get(1));
            Thread.sleep(300);
        }
    }
}

There exists slight difference between Guava and Caffeine

使用上,有一点小小的区别

1) Builder: Guava use CacheBuilder to create new builder while Caffeine use Caffeine.

创建Builder时,Guava使用CacheBuilder,咖啡因使用Caffeine

2) Get method: Guava pass callable for get method

两者的Get方法第二个参数略有区别Guava 传入Callable, 内部可以抛出异常,无需写冗余的try catch,而咖啡因传入Function,必须手动捕获可能的异常

V get(K var1, Callable<? extends V> var2) throws ExecutionException;

Caffeine pass Function for get method

 @CheckForNull
    V get(@Nonnull K var1, @Nonnull Function<? super K, ? extends V> var2);

Callable itself throws exception so we don‘t have to try catch the exception in the block while Function tolerates no exception.

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

Next Chapter, I‘ll compare the async cache.

下一节,博主会进一步比较异步缓存

时间: 2024-11-08 20:12:16

A comparison of local caches (1) 【本地缓存之比较 (1)】的相关文章

本地缓存目录空间不足

本地缓存目录空间不足 在切图的时候,为了提高切图的性能,通常会配置集群,采用多台gis服务器同时执行一个切图任务.这样就有一个问题需要解决,那就是如果这么多gis服务器将自己切好的图,立即同时写入到arcgiscache的目录下.则会导致切图的性能的下降. 因此arcgis server提供了这样的机制: 在每个gis服务器上的临时缓存目录生成一个bundle,然后将一个bundle再拷贝到arcgissserver的arcgiscache目录下 NB: 1. 仅适用于紧凑型缓存 2.arcgi

故障排除 Mybatis ORA-01000 和 本地缓存问题

※异常信息 环境 MyBatis Oracle11.2c Terasoluna BatchCaused by: org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: ORA-00604: 再帰SQLレベル1でエラーが発生しました.ORA-01000: 最大オープン?カーソル数を超えました.ORA-00604: 再帰SQLレベル1で

Java8简单的本地缓存实现

译文出处: 踏雁寻花   原文出处:lukaseder 这里我将会给大家演示用ConcurrentHashMap类和lambda表达式实现一个本地缓存.因为Map有一个新的方法,在key为Null的时候自动计算一个新的value值.非常适合实现cache.来看下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void main(String[] args) {     for (int i = 0; i < 10; i++)   

分布式系统JVM本地缓存同步实现dlcache

现成的分布式K/V缓存已经有很多的实现,最主要的比如redis,memcached.那为什么我们还要自己去实现呢,在我们解决了分布式系统下大量rpc调用导致的高延时后,我们发现很多服务需要大量的访问分布式缓存,由于分布式缓存通常部署在单独的服务器中,在lan中,通常单次网络也需要1ms,一个请求少的可能需要一两次缓存访问,复杂的服务比如委托.出入金.融资等会访问一二十次,即使程序已经优化,但仅访问分布式缓存花费的网络延时占据了整个响应时间的很大一部分比例,而这些需要广泛被访问的数据通常数据量本身

本地缓存方式

iOS本地缓存数据方式有五种: 1.直接写文件方式:可以存储的对象有NSString.NSArray.NSDictionary.NSData.NSNumber,数据全部存放在一个属性列表文件(*.plist文件)中. 2.NSUserDefaults(偏好设置),用来存储应用设置信息,文件放在perference目录下. 3.归档操作(NSkeyedArchiver),不同于前面两种,它可以把自定义对象存放在文件中. 4.coreData:coreData是苹果官方iOS5之后推出的综合型数据库

iOS五种本地缓存数据方式

iOS五种本地缓存数据方式 iOS本地缓存数据方式有五种:前言 1.直接写文件方式:可以存储的对象有NSString.NSArray.NSDictionary.NSData.NSNumber,数据全部存放在一个属性列表文件(*.plist文件)中. 2.NSUserDefaults(偏好设置),用来存储应用设置信息,文件放在perference目录下. 3.归档操作(NSkeyedArchiver),不同于前面两种,它可以把自定义对象存放在文件中. 4.coreData:coreData是苹果官

HTML5离线存储和本地缓存

一.离线存储 有一个web应用有三个文件index.html,a.js,b.css,现在需要把js和css文件缓存起来 1.在index.html里加上<html manifest="test.manifest"> 2.manifest清单格式如下 CACHE MANIFEST #上面一句必须 #v1.0.0 #需要缓存的文件 CACHE: a.js b.css #不需要缓存的文件 NETWORK: * #无法访问页面 FALLBACK: 404.html 3.manife

基于guava实现本地缓存

今天一个接口响应超时,然后我优化,用到了本地缓存. maven 依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.1-jre</version> </dependency> LocalCache 本地缓存工具类 package com.itbac.common.cache;

关于java中的本地缓存-总结概述

java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下.自己构造单例.guava.ehcache基本上涵盖了目前的大多数行为了.   为什么要有本地缓存? 在 系统中,有些数据,数据量小,但是访问十分频繁(例如国家标准行政区域数据),针对这种场景,需要将数据搞到应用的本地缓存中,以提升系统的访问效率,减 少无谓的数据库访问(数据库访问占用数据库连接,同时网络消耗比较大),但是有一点需要注意,就是缓存的占用空间以及缓存的失效策略. 为什么是本地缓存,而