1. 程式人生 > 其它 >guava、caffeine、ohc(堆外快取)詳解

guava、caffeine、ohc(堆外快取)詳解

一、Guava快取

 

Guava Cache適用於以下場景:

  • 你願意消耗一些記憶體空間來提升速度。
  • 你預料到某些鍵會被查詢一次以上。
  • 快取中存放的資料總量不會超出記憶體容量。(Guava Cache是單個應用執行時的本地快取。它不把資料存放到檔案或外部伺服器。如果這不符合你的需求,請嘗試Redis這類工具)

倉庫座標如下:

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
</dependency>

程式碼詳細示例:

@Data
public class CacheVO {

    private String name;

    public CacheVO(String name) {
        this.name = name;
    }


}
public class GuavaCacheMangerService {

    private static LoadingCache<String, CacheVO> cache;

    private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new
            LinkedBlockingQueue<Runnable>(1204));

    static {
        cache = CacheBuilder.newBuilder()
                // 快取項在給定時間內沒有被讀/寫訪問,則回收。
                .expireAfterAccess(500, TimeUnit.SECONDS)
                // 快取項在給定時間內沒有被寫訪問(建立或覆蓋),則回收。
                // 如果認為快取資料總是在固定時候後變得陳舊不可用,這種回收方式是可取的。
                .expireAfterWrite(500, TimeUnit.SECONDS)
                // 初始化容量大小
                .initialCapacity(1024 * 100)
                // 快取項的數目不超過固定值
                .maximumSize(1024 * 100)
                // 可以為快取增加自動重新整理功能,配合CacheLoader reload使用
                .refreshAfterWrite(1, TimeUnit.SECONDS)
                // 載入快取
                .build(new CacheLoader<String, CacheVO>() {
                    // 單個載入,要麼返回已經快取的值,要麼使用CacheLoader向快取原子地載入新值。
                    @Override
                    public CacheVO load(String s) throws Exception {
                        return createCacheVO(s);
                    }

                    // 批量載入,對每個不在快取的鍵,getAll方法會單獨呼叫CacheLoader.load來載入快取項。
                    // 如果批量載入比多個單獨載入更高效,你可以過載CacheLoader.loadAll來利用這一點。
                    @Override
                    public Map<String, CacheVO> loadAll(Iterable<? extends String> keys) throws Exception {
                        return createBatchCacheVOs(keys);
                    }

                    // 非同步重新整理載入新值,在重新整理操作進行時,快取仍然可以向其他執行緒返回舊值,
                    // 而不像回收操作,讀快取的執行緒必須等待新值載入完成。
                    @Override
                    public ListenableFuture<CacheVO> reload(String key, CacheVO oldValue) throws Exception {
                        if (needRefresh()) {
                            return Futures.immediateFuture(oldValue);
                        }
                        ListenableFutureTask<CacheVO> task = ListenableFutureTask.create(() -> {return createCacheVO(key);});
                        executorService.execute(task);
                        return task;
                    }
                });
    }

    public static boolean needRefresh() {
        Random ra =new Random();
        return (ra.nextInt(10) % 2) > 0 ? true : false;
    }

    public static CacheVO createCacheVO(String key){
        return new CacheVO(key);
    }

    public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {
        Map<String, CacheVO> result = new HashMap<>();
        for (String key : keys) {
            result.put(key, new CacheVO(key));
        }
        return result;
    }
    
    public static void main(String[] args) throws Exception{
        // 單個獲取
        CacheVO cacheVO1 = cache.get("AA");

        // 如果有快取則返回;否則運算、快取、然後返回,整個過程是阻塞的
        // 在整個載入方法完成前,快取項相關的可觀察狀態都不會更改。
        CacheVO cacheVO2 = cache.get("BB", () -> {return createCacheVO("BB");});

        List<String> list = new ArrayList<>();
        list.add("CC");
        list.add("DD");
        // 批量獲取
        Map<String, CacheVO> cacheMap = cache.getAll(list);

        // 個別清除
        cache.invalidate("AA");
        // 批量清除
        cache.invalidateAll(list);
        // 清除所有
        cache.invalidateAll();

    }
}

二、Caffeine快取

Caffeine是一種高效能的快取庫,是基於Java 8的最佳(最優)快取框架,效能各方面優於guava。

程式碼倉庫如下:

<dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>2.4.0</version>
</dependency>

程式碼詳細示例如下:

public class CaffeineCacheMangerService {

    private static LoadingCache<String, CacheVO> cache;

    private static AsyncLoadingCache<String, CacheVO> asyncCache;

    private static AsyncLoadingCache<String, CacheVO> asyncCache1;

    private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new
            LinkedBlockingQueue<Runnable>(1204));

    static {
        cache = Caffeine.newBuilder()
                // 初始化快取長度
                .initialCapacity(1024 * 10)
                // 最大長度
                .maximumSize(1024 * 10)
                // 更新策略
                .refreshAfterWrite(10, TimeUnit.SECONDS)
                // 設定快取的過期時間
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .build(new CacheLoader<String, CacheVO>() {

                    // 同步載入
                    @CheckForNull
                    @Override
                    public CacheVO load(@Nonnull String key) throws Exception {
                        return createCacheVO(key);
                    }

                    // getAll將會對快取中沒有值的key分別呼叫CacheLoader.load方法來構建快取的值。
                    // 我們可以重寫CacheLoader.loadAll方法來提高getAll的效率。
                    @Nonnull
                    @Override
                    public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) throws Exception {
                        return createBatchCacheVOs(keys);
                    }
                });

        // 非同步載入 同步load寫法,最後也會轉非同步
        asyncCache = Caffeine.newBuilder()
                .maximumSize(1024 * 10)
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .buildAsync(new CacheLoader<String, CacheVO>() {
                    @CheckForNull
                    @Override
                    public CacheVO load(@Nonnull String key) throws Exception {
                        return createCacheVO(key);
                    }

                    @Nonnull
                    @Override
                    public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) {
                        return createBatchCacheVOs(keys);
                    }
                });

        // 非同步載入 非同步load寫法
        asyncCache1 = Caffeine.newBuilder()
                .maximumSize(1024 * 10)
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .buildAsync(new AsyncCacheLoader<String, CacheVO>() {
                    @Nonnull
                    @Override
                    public CompletableFuture<CacheVO> asyncLoad(@Nonnull String key, @Nonnull Executor executor) {
                        return asyncCreateCacheVO(key, executor);
                    }

                    @Nonnull
                    @Override
                    public CompletableFuture<Map<String, CacheVO>> asyncLoadAll(@Nonnull Iterable<? extends String> keys, @Nonnull Executor executor) {
                        return asyncCreateBatchCacheVOs(keys, executor);
                    }
                });

    }

    public static CompletableFuture<CacheVO> asyncCreateCacheVO(String key, Executor executor) {
        return CompletableFuture.supplyAsync(() -> createCacheVO(key), executor);
    }

    public static CompletableFuture<Map<String, CacheVO>> asyncCreateBatchCacheVOs(Iterable<? extends String> keys, Executor executor) {
        return CompletableFuture.supplyAsync(() -> createBatchCacheVOs(keys),executor);
    }

    public static CacheVO createCacheVO(String key) {
        return new CacheVO(key);
    }

    public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {
        Map<String, CacheVO> result = new HashMap<>();
        for (String key : keys) {
            result.put(key, new CacheVO(key));
        }
        return result;
    }

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

        CacheVO cacheVO1 = cache.get("AA");

        List<String> list = new ArrayList<>();
        list.add("BB");
        list.add("CC");
        Map<String, CacheVO> map = cache.getAll(list);

        // 如果有快取則返回;否則運算、快取、然後返回,整個過程是阻塞的
        // 即使多個執行緒同時請求該值也只會呼叫一次Function方法
        

 

Guava Cache適用於以下場景:

  • 你願意消耗一些記憶體空間來提升速度。
  • 你預料到某些鍵會被查詢一次以上。
  • 快取中存放的資料總量不會超出記憶體容量。(Guava Cache是單個應用執行時的本地快取。它不把資料存放到檔案或外部伺服器。如果這不符合你的需求,請嘗試Redis這類工具)

倉庫座標如下:

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
</dependency>

程式碼詳細示例:

@Data
public class CacheVO {

    private String name;

    public CacheVO(String name) {
        this.name = name;
    }


}
public class GuavaCacheMangerService {

    private static LoadingCache<String, CacheVO> cache;

    private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new
            LinkedBlockingQueue<Runnable>(1204));

    static {
        cache = CacheBuilder.newBuilder()
                // 快取項在給定時間內沒有被讀/寫訪問,則回收。
                .expireAfterAccess(500, TimeUnit.SECONDS)
                // 快取項在給定時間內沒有被寫訪問(建立或覆蓋),則回收。
                // 如果認為快取資料總是在固定時候後變得陳舊不可用,這種回收方式是可取的。
                .expireAfterWrite(500, TimeUnit.SECONDS)
                // 初始化容量大小
                .initialCapacity(1024 * 100)
                // 快取項的數目不超過固定值
                .maximumSize(1024 * 100)
                // 可以為快取增加自動重新整理功能,配合CacheLoader reload使用
                .refreshAfterWrite(1, TimeUnit.SECONDS)
                // 載入快取
                .build(new CacheLoader<String, CacheVO>() {
                    // 單個載入,要麼返回已經快取的值,要麼使用CacheLoader向快取原子地載入新值。
                    @Override
                    public CacheVO load(String s) throws Exception {
                        return createCacheVO(s);
                    }

                    // 批量載入,對每個不在快取的鍵,getAll方法會單獨呼叫CacheLoader.load來載入快取項。
                    // 如果批量載入比多個單獨載入更高效,你可以過載CacheLoader.loadAll來利用這一點。
                    @Override
                    public Map<String, CacheVO> loadAll(Iterable<? extends String> keys) throws Exception {
                        return createBatchCacheVOs(keys);
                    }

                    // 非同步重新整理載入新值,在重新整理操作進行時,快取仍然可以向其他執行緒返回舊值,
                    // 而不像回收操作,讀快取的執行緒必須等待新值載入完成。
                    @Override
                    public ListenableFuture<CacheVO> reload(String key, CacheVO oldValue) throws Exception {
                        if (needRefresh()) {
                            return Futures.immediateFuture(oldValue);
                        }
                        ListenableFutureTask<CacheVO> task = ListenableFutureTask.create(() -> {return createCacheVO(key);});
                        executorService.execute(task);
                        return task;
                    }
                });
    }

    public static boolean needRefresh() {
        Random ra =new Random();
        return (ra.nextInt(10) % 2) > 0 ? true : false;
    }

    public static CacheVO createCacheVO(String key){
        return new CacheVO(key);
    }

    public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {
        Map<String, CacheVO> result = new HashMap<>();
        for (String key : keys) {
            result.put(key, new CacheVO(key));
        }
        return result;
    }
    
    public static void main(String[] args) throws Exception{
        // 單個獲取
        CacheVO cacheVO1 = cache.get("AA");

        // 如果有快取則返回;否則運算、快取、然後返回,整個過程是阻塞的
        // 在整個載入方法完成前,快取項相關的可觀察狀態都不會更改。
        CacheVO cacheVO2 = cache.get("BB", () -> {return createCacheVO("BB");});

        List<String> list = new ArrayList<>();
        list.add("CC");
        list.add("DD");
        // 批量獲取
        Map<String, CacheVO> cacheMap = cache.getAll(list);

        // 個別清除
        cache.invalidate("AA");
        // 批量清除
        cache.invalidateAll(list);
        // 清除所有
        cache.invalidateAll();

    }
}

二、Caffeine快取

Caffeine是一種高效能的快取庫,是基於Java 8的最佳(最優)快取框架,效能各方面優於guava。

程式碼倉庫如下:

<dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>2.4.0</version>
</dependency>

程式碼詳細示例如下:

public class CaffeineCacheMangerService {

    private static LoadingCache<String, CacheVO> cache;

    private static AsyncLoadingCache<String, CacheVO> asyncCache;

    private static AsyncLoadingCache<String, CacheVO> asyncCache1;

    private static ExecutorService executorService = new ThreadPoolExecutor(8, 8, 8, TimeUnit.SECONDS, new
            LinkedBlockingQueue<Runnable>(1204));

    static {
        cache = Caffeine.newBuilder()
                // 初始化快取長度
                .initialCapacity(1024 * 10)
                // 最大長度
                .maximumSize(1024 * 10)
                // 更新策略
                .refreshAfterWrite(10, TimeUnit.SECONDS)
                // 設定快取的過期時間
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .build(new CacheLoader<String, CacheVO>() {

                    // 同步載入
                    @CheckForNull
                    @Override
                    public CacheVO load(@Nonnull String key) throws Exception {
                        return createCacheVO(key);
                    }

                    // getAll將會對快取中沒有值的key分別呼叫CacheLoader.load方法來構建快取的值。
                    // 我們可以重寫CacheLoader.loadAll方法來提高getAll的效率。
                    @Nonnull
                    @Override
                    public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) throws Exception {
                        return createBatchCacheVOs(keys);
                    }
                });

        // 非同步載入 同步load寫法,最後也會轉非同步
        asyncCache = Caffeine.newBuilder()
                .maximumSize(1024 * 10)
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .buildAsync(new CacheLoader<String, CacheVO>() {
                    @CheckForNull
                    @Override
                    public CacheVO load(@Nonnull String key) throws Exception {
                        return createCacheVO(key);
                    }

                    @Nonnull
                    @Override
                    public Map<String, CacheVO> loadAll(@Nonnull Iterable<? extends String> keys) {
                        return createBatchCacheVOs(keys);
                    }
                });

        // 非同步載入 非同步load寫法
        asyncCache1 = Caffeine.newBuilder()
                .maximumSize(1024 * 10)
                .expireAfterWrite(10, TimeUnit.SECONDS)
                .buildAsync(new AsyncCacheLoader<String, CacheVO>() {
                    @Nonnull
                    @Override
                    public CompletableFuture<CacheVO> asyncLoad(@Nonnull String key, @Nonnull Executor executor) {
                        return asyncCreateCacheVO(key, executor);
                    }

                    @Nonnull
                    @Override
                    public CompletableFuture<Map<String, CacheVO>> asyncLoadAll(@Nonnull Iterable<? extends String> keys, @Nonnull Executor executor) {
                        return asyncCreateBatchCacheVOs(keys, executor);
                    }
                });

    }

    public static CompletableFuture<CacheVO> asyncCreateCacheVO(String key, Executor executor) {
        return CompletableFuture.supplyAsync(() -> createCacheVO(key), executor);
    }

    public static CompletableFuture<Map<String, CacheVO>> asyncCreateBatchCacheVOs(Iterable<? extends String> keys, Executor executor) {
        return CompletableFuture.supplyAsync(() -> createBatchCacheVOs(keys),executor);
    }

    public static CacheVO createCacheVO(String key) {
        return new CacheVO(key);
    }

    public static Map<String, CacheVO> createBatchCacheVOs(Iterable<? extends String> keys) {
        Map<String, CacheVO> result = new HashMap<>();
        for (String key : keys) {
            result.put(key, new CacheVO(key));
        }
        return result;
    }

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

        CacheVO cacheVO1 = cache.get("AA");

        List<String> list = new ArrayList<>();
        list.add("BB");
        list.add("CC");
        Map<String, CacheVO> map = cache.getAll(list);

        // 如果有快取則返回;否則運算、快取、然後返回,整個過程是阻塞的
        // 即使多個執行緒同時請求該值也只會呼叫一次Function方法