Ehcache3開發入門簡介


在高並發應用中緩存就是核心機制。最近在研究Ehcache,發現這是一個更加靈活易用的緩存框架(相對於Redis、Memcache),Ehcache更加小巧輕便。而且都有持久化機制,不用擔心JVM和服務器重啟的數據丟失。我用四個字來形容:拎包入住。

著名的Hibernate的默認緩存策略就是用Ehcache,Liferay的緩存也是依賴Ehcache,可見其健壯性。與其黑盒的瞎眼使用,不如來研究下這后邊的機制。

Ehcache的架構

主要的特點:

  1. 緩存數據有三級:內存、堆外緩存Off-Heap、Disk緩存,因此無需擔心容量問題。還可以通過RMI、可插入API等方式進行分布式緩存。
  2. 緩存數據會在虛擬機重啟的過程中寫入磁盤,持久化。
  3. 具有緩存和緩存管理器的偵聽接口。
  4. 支持多緩存管理器實例,以及一個實例的多個緩存區域。
Maven寫法:
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.3.0</version>
</dependency>

Gradle寫法:
compile group: 'org.ehcache', name: 'ehcache', version: '3.3.0'

 還需注意,工程要有slf4j-api-1.7.XX的依賴。

 

通用的讀寫使用CacheManager

兼容3.0和2.0版
代碼可讀性很好,就不解釋了,詳情見官網API:http://www.ehcache.org/documentation/3.3

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.UserManagedCache;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.builders.UserManagedCacheBuilder;

public class Main {

    public static void main(String[] args) {
        CacheManager cacheManager
        = CacheManagerBuilder.newCacheManagerBuilder() 
        .withCache("preConfigured",
            CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(10))) 
        .build(); 
    cacheManager.init();
    
    Cache<Long, String> preConfigured =
        cacheManager.getCache("preConfigured", Long.class, String.class); 

    Cache<Integer, String> myCache = cacheManager.createCache("myCache", 
        CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, ResourcePoolsBuilder.heap(10)).build());

    for (int i=0;i<=20;i++){
        //
        myCache.put(i, "@"+i);
        //
        String value = myCache.get(i);
        System.out.println("get at "+i+":"+value);
    }
    
    cacheManager.removeCache("preConfigured"); 
    cacheManager.close(); 
    }

}

 

3.0的讀寫新泛型方法UserManagedCache

很明顯,更加簡潔了

import org.ehcache.UserManagedCache;
import org.ehcache.config.builders.UserManagedCacheBuilder;


public class Main {

    public static void main(String[] args) {
        UserManagedCache<Integer, String> userManagedCache =
            UserManagedCacheBuilder.newUserManagedCacheBuilder(Integer.class, String.class)
                .build(false); 
        userManagedCache.init();         

        for (int i=0;i<=20;i++){
            //
            userManagedCache.put(i, "#"+i);
            //
            String value = userManagedCache.get(i);
            System.out.println("get at "+i+":"+value);
        }

        userManagedCache.close();
    }

}

 

三層緩存策略

之前的版本是2級緩存,內存和磁盤,新版增加了更靈活的一級,堆外緩存(off-heap),這既是獨立的進程緩存,還是JVM堆外的系統緩存,可以想象一下,JVM堆是非常寶貴的,如果占用過大會帶來GC性能問題,堆外緩存很好的解決了這個問題,現在服務器內存越來越大,而磁盤緩存的io性能又比較低,off-heap緩存就是折中的方案,既保證了高速性能,又可以有一定的容量。off-heap緩存性能的占用主要是序列化、反序列化的過程。一旦對象被序列化,在返回Java堆的時候必需反序列化才可以使用。這是一筆性能開銷。但off-heap還是要比本地磁盤、網絡存儲、RDBMS數據庫IO等記錄數據的系統要快非常多。還應指出的是,序列化/反序列化的性能開銷遠沒有很多用戶想象的那么大。off-heap已經針對字節緩沖區做了優化,本身也包含一些優化機制,可以對使用標准Java序列化的對象進行優化,能使復雜Java對象的性能提升兩倍,使byte數組的性能提升四倍。

個人是這樣理解的:

  1. 常被查詢、最重要、數據量較小的數據存放在堆緩存,不用擔心JVM的重啟,有持久化機制;
  2. 常被查詢、數據量中等的數據存放在堆外緩存,幾個G就好了,不用擔心服務器的重啟,有持久化機制;
  3. 不常用、大量的數據、但又不想占用數據庫IO的數據,放在Disk緩存,容量自便;

  理解了三級緩存機制,現在還有一個黑盒問題有待了解,即EHcache的持久化策略算法,Redis的持久化策略是2種,即RDB快照和AOF追加日志,讓用戶在性能和完整性之間來自由選擇,這非常靈活。目前還沒查到EHcache的持久化策略,本好奇貓有點沮喪。

import java.io.File;
import org.ehcache.Cache;
import org.ehcache.PersistentCacheManager;
import org.ehcache.UserManagedCache;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.builders.UserManagedCacheBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;

public class Main {

    public static void main(String[] args) {

        PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
        .with(CacheManagerBuilder.persistence(getStoragePath() + File.separator + "myData")) 
        .withCache("threeTieredCache",
            CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class,
                ResourcePoolsBuilder.newResourcePoolsBuilder()
                    .heap(10, EntryUnit.ENTRIES)  //
                    .offheap(1, MemoryUnit.MB)    //堆外
                    .disk(20, MemoryUnit.GB)      //磁盤
                )
        ).build(true);

        Cache<Integer, String> threeTieredCache = persistentCacheManager.getCache("threeTieredCache", Integer.class, String.class);

        //
        for (int i=0;i<=20000;i++){
            threeTieredCache.put(i, "$"+i);
        }
        
        //
        for (int i=0;i<=200000;i++){
            String value = threeTieredCache.get(i);
            System.out.println("get at "+i+":"+value);
        }

        persistentCacheManager.close();
    }

    private static String getStoragePath() {
        // TODO Auto-generated method stub
        return "d:";
    }

}

發現數據量大了之后會寫入磁盤:

一些實戰使用方法,歡迎拍磚

一般MVC應用的后端是Model - Persistent DAO - Service的分層
比如,我們有User的Model,UserService的Service。

//寫方式:

user = new User
userService.put(k, v) //系統持久化
cache.put(k, v) //緩存寫入


//讀:

user = cache.get(k)
if(user == null) {
    user = userService.get(k)
    cache.put(k, v)
}

 

集群

不用集群的多級緩存

應用程序直接訪問一個或者多個Cache Manager,而一個Cache Manager管理Caches集合。Caches集合可以多級存儲。

使用集群

 

一些更高級的用法

事務緩存

對讀寫原子性有要求的必讀:
http://www.ehcache.org/documentation/3.3/xa.html

線程池的使用

用於異步操作,這將大幅度提升效率:
http://www.ehcache.org/documentation/3.3/thread-pools.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM