内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

如何理解應(yīng)用級緩存

本篇內(nèi)容主要講解“如何理解應(yīng)用級緩存”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何理解應(yīng)用級緩存”吧!

創(chuàng)新互聯(lián)長期為數(shù)千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為疏附企業(yè)提供專業(yè)的做網(wǎng)站、成都網(wǎng)站建設(shè),疏附網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

緩存的命中率

緩存的命中率是指從緩存中獲取到數(shù)據(jù)的次數(shù)和總讀取次數(shù)的比率,命中率越高證明緩存的效果越好。這是一個很重要的指標(biāo),應(yīng)該通過監(jiān)控這個指標(biāo)來判斷我們的緩存是否設(shè)置的合理。

緩存的回收策略

基于時間

  • 存活期:在設(shè)置緩存的同時設(shè)置該緩存可以存活多久,不論在存活期內(nèi)被訪問了多少次,時間到了都會過期

  • 空閑期:是指緩存的數(shù)據(jù)多久沒有被訪問就過期

基于空間

設(shè)置緩存的存儲空間,比如:設(shè)置緩存的空間是 1G,當(dāng)達(dá)到了1G之后就會按照一定的策略將部分?jǐn)?shù)據(jù)移除

基于緩存數(shù)量

設(shè)置緩存的最大條目數(shù),當(dāng)達(dá)到了設(shè)置的最大條目數(shù)之后按照一定的策略將舊的數(shù)據(jù)移除

基于Java對象引用

  • 弱引用:當(dāng)垃圾回收器開始回收內(nèi)存的時候,如果發(fā)現(xiàn)了弱引用,它將立即被回收。

  • 軟引用:當(dāng)垃圾回收器發(fā)現(xiàn)內(nèi)存已不足的情況下會回收軟引用的對象,從而騰出一下空間,防止發(fā)生內(nèi)存溢出。軟引用適合用來做堆緩存

緩存的回收算法

  • FIFO 先進(jìn)先出算法

  • LRU 最近最少使用算法

  • LFU 最不常用算法

Java緩存的類型

堆緩存

堆緩存是指把數(shù)據(jù)緩存在JVM的堆內(nèi)存中,使用堆緩存的好處是沒有序列化和反序列化的操作,是最快的緩存。如果緩存的數(shù)據(jù)量很大,為了避免造成OOM通常情況下使用的時軟引用來存儲緩存對象;堆緩存的缺點(diǎn)是緩存的空間有限,并且垃圾回收器暫停的時間會變長。

Gauva Cache實(shí)現(xiàn)堆緩存
Cache<string, string> cache = CacheBuilder.newBuilder()
                .build();

通過CacheBuilder構(gòu)建緩存對象

Gauva Cache的主要配置和方法

  • put : 向緩存中設(shè)置key-value

  • V get(K key, Callable<!--? extends V--> loader) : 獲取一個緩存值,如果緩存中沒有,那么就調(diào)用loader獲取一個然后放入到緩存

  • expireAfterWrite : 設(shè)置緩存的存活期,寫入數(shù)據(jù)后指定時間之后失效

  • expireAfterAccess : 設(shè)置緩存的空閑期,在給定的時間內(nèi)沒有被訪問就會被回收

  • maximumSize : 設(shè)置緩存的最大條目數(shù)

  • weakKeys/weakValues : 設(shè)置弱引用緩存

  • softValues : 設(shè)置軟引用緩存

  • invalidate/invalidateAll: 主動失效指定key的緩存數(shù)據(jù)

  • recordStats : 啟動記錄統(tǒng)計信息,可以查看到命中率

  • removalListener : 當(dāng)緩存被刪除的時候會調(diào)用此監(jiān)聽器,可以用于查看為什么緩存會被刪除

Caffeine實(shí)現(xiàn)堆緩存

Caffeine是使用Java8對Guava緩存的重寫版本,高性能Java本地緩存組件,也是Spring推薦的堆緩存的實(shí)現(xiàn),與spring的集成可以查看文檔https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-store-configuration-caffeine。

由于是對Guava緩存的重寫版本,所以很多的配置參數(shù)都是和Guava緩存一致:

  • initialCapacity: 初始的緩存空間大小

  • maximumSize: 緩存的最大條數(shù)

  • maximumWeight: 緩存的最大權(quán)重

  • expireAfterAccess: 最后一次寫入或訪問后經(jīng)過固定時間過期

  • expireAfterWrite: 最后一次寫入后經(jīng)過固定時間過期

  • expireAfter : 自定義過期策略

  • refreshAfterWrite: 創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過固定的時間間隔,刷新緩存

  • weakKeys: 打開key的弱引用

  • weakValues:打開value的弱引用

  • softValues:打開value的軟引用

  • recordStats:開啟統(tǒng)計功能

Caffeine的官方文檔:https://github.com/ben-manes/caffeine/wiki

  1. pom.xml中添加依賴

<dependency>
    <groupid>com.github.ben-manes.caffeine</groupid>
    <artifactid>caffeine</artifactid>
    <version>2.8.4</version>
</dependency>
  1. Caffeine Cache提供了三種緩存填充策略:手動、同步加載和異步加載。

  • 手動加載:在每次get key的時候指定一個同步的函數(shù),如果key不存在就調(diào)用這個函數(shù)生成一個值

public Object manual(String key) {
    Cache<string, object> cache = Caffeine.newBuilder()
            .expireAfterAccess(1, TimeUnit.SECONDS) //設(shè)置空閑期時長
            .maximumSize(10)
            .build();
    return cache.get(key, t -&gt; setValue(key).apply(key));
}

public Function<string, object> setValue(String key){
    return t -&gt; "https://silently9527.cn";
}
  • 同步加載:構(gòu)造Cache時候,build方法傳入一個CacheLoader實(shí)現(xiàn)類。實(shí)現(xiàn)load方法,通過key加載value。

public Object sync(String key){
    LoadingCache<string, object> cache = Caffeine.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(1, TimeUnit.MINUTES) //設(shè)置存活期時長
            .build(k -&gt; setValue(key).apply(key));
    return cache.get(key);
}

public Function<string, object> setValue(String key){
    return t -&gt; "https://silently9527.cn";
}
  • 異步加載:AsyncLoadingCache是繼承自LoadingCache類的,異步加載使用Executor去調(diào)用方法并返回一個CompletableFuture

public CompletableFuture async(String key) {
    AsyncLoadingCache<string, object> cache = Caffeine.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .buildAsync(k -&gt; setAsyncValue().get());
    return cache.get(key);
}

public CompletableFuture<object> setAsyncValue() {
    return CompletableFuture.supplyAsync(() -&gt; "公眾號:貝塔學(xué)JAVA");
}
  1. 監(jiān)聽緩存被清理的事件

public void removeListener() {
    Cache<string, object> cache = Caffeine.newBuilder()
            .removalListener((String key, Object value, RemovalCause cause) -&gt; {
                System.out.println("remove lisitener");
                System.out.println("remove Key:" + key);
                System.out.println("remove Value:" + value);
            })
            .build();
    cache.put("name", "silently9527");
    cache.invalidate("name");
}
  1. 統(tǒng)計

public void recordStats() {
    Cache<string, object> cache = Caffeine.newBuilder()
            .maximumSize(10000)
            .recordStats()
            .build();
    cache.put("公眾號", "貝塔學(xué)JAVA");
    cache.get("公眾號", (t) -&gt; "");
    cache.get("name", (t) -&gt; "silently9527");

    CacheStats stats = cache.stats();
    System.out.println(stats);
}

通過 Cache.stats() 獲取到CacheStatsCacheStats提供以下統(tǒng)計方法:

  • hitRate(): 返回緩存命中率

  • evictionCount(): 緩存回收數(shù)量

  • averageLoadPenalty(): 加載新值的平均時間

EhCache實(shí)現(xiàn)堆緩存

EhCache 是老牌Java開源緩存框架,早在2003年就已經(jīng)出現(xiàn)了,發(fā)展到現(xiàn)在已經(jīng)非常成熟穩(wěn)定,在Java應(yīng)用領(lǐng)域應(yīng)用也非常廣泛,而且和主流的Java框架比如Srping可以很好集成。相比于 Guava Cache,EnCache 支持的功能更豐富,包括堆外緩存、磁盤緩存,當(dāng)然使用起來要更重一些。使用 Ehcache 的Maven 依賴如下:

<dependency>
    <groupid>org.ehcache</groupid>
    <artifactid>ehcache</artifactid>
    <version>3.6.3</version>
</dependency>
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);

ResourcePoolsBuilder resource = ResourcePoolsBuilder.heap(10); //設(shè)置最大緩存條目數(shù)

CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(String.class, String.class, resource)
        .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(10)))
        .build();

Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig);
  • ResourcePoolsBuilder.heap(10)設(shè)置緩存的最大條目數(shù),這是簡寫方式,等價于ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES);

  • ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB)設(shè)置緩存最大的空間10MB

  • withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(10))) 設(shè)置緩存空閑時間

  • withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10))) 設(shè)置緩存存活時間

  • remove/removeAll主動失效緩存,與Guava Cache類似,調(diào)用方法后不會立即去清除回收,只有在get或者put的時候判斷緩存是否過期

  • withSizeOfMaxObjectSize(10,MemoryUnit.KB)限制單個緩存對象的大小,超過這兩個限制的對象則不被緩存

堆外緩存

堆外緩存即緩存數(shù)據(jù)在堆外內(nèi)存中,空間大小只受本機(jī)內(nèi)存大小限制,不受GC管理,使用堆外緩存可以減少GC暫停時間,但是堆外內(nèi)存中的對象都需要序列化和反序列化,KEY和VALUE必須實(shí)現(xiàn)Serializable接口,因此速度會比堆內(nèi)緩存慢。在Java中可以通過 -XX:MaxDirectMemorySize 參數(shù)設(shè)置堆外內(nèi)存的上限

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
// 堆外內(nèi)存不能按照存儲條目限制,只能按照內(nèi)存大小進(jìn)行限制,超過限制則回收緩存
ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB);

CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(String.class, String.class, resource)
        .withDispatcherConcurrency(4)
        .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
        .withSizeOfMaxObjectSize(10, MemoryUnit.KB)
        .build();

Cache<string, string> cache = cacheManager.createCache("userInfo2", cacheConfig);
cache.put("website", "https://silently9527.cn");
System.out.println(cache.get("website"));

磁盤緩存

把緩存數(shù)據(jù)存放到磁盤上,在JVM重啟時緩存的數(shù)據(jù)不會受到影響,而堆緩存和堆外緩存都會丟失;并且磁盤緩存有更大的存儲空間;但是緩存在磁盤上的數(shù)據(jù)也需要支持序列化,速度會被比內(nèi)存更慢,在使用時推薦使用更快的磁盤帶來更大的吞吐率,比如使用閃存代替機(jī)械磁盤。

CacheManagerConfiguration<persistentcachemanager> persistentManagerConfig = CacheManagerBuilder
        .persistence(new File("/Users/huaan9527/Desktop", "ehcache-cache"));

PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
        .with(persistentManagerConfig).build(true);

//disk 第三個參數(shù)設(shè)置為 true 表示將數(shù)據(jù)持久化到磁盤上
ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().disk(100, MemoryUnit.MB, true);

CacheConfiguration<string, string> config = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(String.class, String.class, resource).build();
Cache<string, string> cache = persistentCacheManager.createCache("userInfo",
        CacheConfigurationBuilder.newCacheConfigurationBuilder(config));

cache.put("公眾號", "貝塔學(xué)JAVA");
System.out.println(cache.get("公眾號"));
persistentCacheManager.close();

在JVM停止時,一定要記得調(diào)用persistentCacheManager.close(),保證內(nèi)存中的數(shù)據(jù)能夠dump到磁盤上。

如何理解應(yīng)用級緩存 這是典型 heap + offheap + disk 組合的結(jié)構(gòu)圖,上層比下層速度快,下層比上層存儲空間大,在ehcache中,空間大小設(shè)置 heap &gt; offheap &gt; disk,否則會報錯; ehcache 會將最熱的數(shù)據(jù)保存在高一級的緩存。這種結(jié)構(gòu)的代碼如下:

CacheManagerConfiguration<persistentcachemanager> persistentManagerConfig = CacheManagerBuilder
        .persistence(new File("/Users/huaan9527/Desktop", "ehcache-cache"));

PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
        .with(persistentManagerConfig).build(true);

ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder()
        .heap(10, MemoryUnit.MB)
        .offheap(100, MemoryUnit.MB)
        //第三個參數(shù)設(shè)置為true,支持持久化
        .disk(500, MemoryUnit.MB, true);

CacheConfiguration<string, string> config = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(String.class, String.class, resource).build();

Cache<string, string> cache = persistentCacheManager.createCache("userInfo",
        CacheConfigurationBuilder.newCacheConfigurationBuilder(config));

//寫入緩存
cache.put("name", "silently9527");
// 讀取緩存
System.out.println(cache.get("name"));

// 再程序關(guān)閉前,需要手動釋放資源
persistentCacheManager.close();

分布式集中緩存

前面提到的堆內(nèi)緩存和堆外緩存如果在多個JVM實(shí)例的情況下會有兩個問題:1.單機(jī)容量畢竟有限;2.多臺JVM實(shí)例緩存的數(shù)據(jù)可能不一致;3.如果緩存數(shù)據(jù)同一時間都失效了,那么請求都會打到數(shù)據(jù)庫上,數(shù)據(jù)庫壓力增大。這時候我們就需要引入分布式緩存來解決,現(xiàn)在使用最多的分布式緩存是redis

如何理解應(yīng)用級緩存

當(dāng)引入分布式緩存之后就可以把應(yīng)用緩存的架構(gòu)調(diào)整成上面的結(jié)構(gòu)。

緩存使用模式的實(shí)踐

緩存使用的模式大概分為兩類:Cache-Aside、Cache-As-SoR(SoR表示實(shí)際存儲數(shù)據(jù)的系統(tǒng),也就是數(shù)據(jù)源)

Cache-Aside

業(yè)務(wù)代碼圍繞著緩存來寫,通常都是從緩存中來獲取數(shù)據(jù),如果緩存沒有命中,則從數(shù)據(jù)庫中查找,查詢到之后就把數(shù)據(jù)放入到緩存;當(dāng)數(shù)據(jù)被更新之后,也需要對應(yīng)的去更新緩存中的數(shù)據(jù)。這種模式也是我們通常使用最多的。

  • 讀場景

value = cache.get(key); //從緩存中讀取數(shù)據(jù)
if(value == null) {
    value = loadFromDatabase(key); //從數(shù)據(jù)庫中查詢
    cache.put(key, value); //放入到緩存中
}
  • 寫場景

wirteToDatabase(key, value); //寫入到數(shù)據(jù)庫
cache.put(key, value); //放入到緩存中 或者 可以刪除掉緩存 cache.remove(key) ,再讀取的時候再查一次

Spring的Cache擴(kuò)展就是使用的Cache-Aside模式,Spring為了把業(yè)務(wù)代碼和緩存的讀取更新分離,對Cache-Aside模式使用AOP進(jìn)行了封裝,提供了多個注解來實(shí)現(xiàn)讀寫場景。官方參考文檔:

  • @Cacheable : 通常是放在查詢方法上,實(shí)現(xiàn)的就是Cache-Aside讀的場景,先查緩存,如果不存在在查詢數(shù)據(jù)庫,最后把查詢出來的結(jié)果放入到緩存。

  • @CachePut : 通常用在保存更新方法上面,實(shí)現(xiàn)的就是Cache-Aside寫的場景,更新完成數(shù)據(jù)庫后把數(shù)據(jù)放入到緩存中。

  • @CacheEvict : 從緩存中刪除指定key的緩存

> 對于一些允許有一點(diǎn)點(diǎn)更新延遲基礎(chǔ)數(shù)據(jù)可以考慮使用canal訂閱binlog日志來完成緩存的增量更新。 > > Cache-Aside還有個問題,如果某個時刻熱點(diǎn)數(shù)據(jù)緩存失效,那么會有很多請求同時打到后端數(shù)據(jù)庫上,數(shù)據(jù)庫的壓力會瞬間增大

Cache-As-SoR

Cache-As-SoR模式也就會把Cache看做是數(shù)據(jù)源,所有的操作都是針對緩存,Cache在委托給真正的SoR去實(shí)現(xiàn)讀或者寫。業(yè)務(wù)代碼中只會看到Cache的操作,這種模式又分為了三種

Read Through

應(yīng)用程序始終從緩存中請求數(shù)據(jù),如果緩存中沒有數(shù)據(jù),則它負(fù)責(zé)使用提供的數(shù)據(jù)加載程序從數(shù)據(jù)庫中檢索數(shù)據(jù),檢索數(shù)據(jù)后,緩存會自行更新并將數(shù)據(jù)返回給調(diào)用的應(yīng)用程序。Gauva Cache、Caffeine、EhCache都支持這種模式;

  1. Caffeine實(shí)現(xiàn)Read Through 由于Gauva Cache和Caffeine實(shí)現(xiàn)類似,所以這里只展示Caffeine的實(shí)現(xiàn),以下代碼來自Caffeine官方文檔

LoadingCache<key, graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(key -&gt; createExpensiveGraph(key));

// Lookup and compute an entry if absent, or null if not computable
Graph graph = cache.get(key);
// Lookup and compute entries that are absent
Map<key, graph> graphs = cache.getAll(keys);

在build Cache的時候指定一個CacheLoader

  • [1] 在應(yīng)用程序中直接調(diào)用cache.get(key)

  • [2] 首先查詢緩存,如果緩存存在就直接返回數(shù)據(jù)

  • [3] 如果不存在,就會委托給CacheLoader去數(shù)據(jù)源中查詢數(shù)據(jù),之后在放入到緩存,返回給應(yīng)用程序

> CacheLoader不要直接返回null,建議封裝成自己定義的Null對像,在放入到緩存中,可以防止緩存擊穿

為了防止因?yàn)槟硞€熱點(diǎn)數(shù)據(jù)失效導(dǎo)致后端數(shù)據(jù)庫壓力增大的情況,我可以在CacheLoader中使用鎖限制只允許一個請求去查詢數(shù)據(jù)庫,其他的請求都等待第一個請求查詢完成后從緩存中獲取,在上一篇 《萬字長文聊緩存(上)》中我們聊到了Nginx也有類似的配置參數(shù)

value = loadFromCache(key);
if(value != null) {
    return value;
}
synchronized (lock) {
    value = loadFromCache(key);
    if(value != null) {
        return value;
    }
    return loadFromDatabase(key);
}
  1. EhCache實(shí)現(xiàn)Read Through

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB); //設(shè)置最大緩存條目數(shù)
CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(String.class, String.class, resource)
        .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
        .withLoaderWriter(new CacheLoaderWriter<string, string>(){
            @Override
            public String load(String key) throws Exception {
                //load from database
                return "silently9527";
            }

            @Override
            public void write(String key, String value) throws Exception {

            }

            @Override
            public void delete(String key) throws Exception {

            }
        })
        .build();

Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig);
System.out.println(cache.get("name"));

在EhCache中使用的是CacheLoaderWriter來從數(shù)據(jù)庫中加載數(shù)據(jù);解決因?yàn)槟硞€熱點(diǎn)數(shù)據(jù)失效導(dǎo)致后端數(shù)據(jù)庫壓力增大的問題和上面的方式一樣,也可以在load中實(shí)現(xiàn)。

Write Through

和Read Through模式類似,當(dāng)數(shù)據(jù)進(jìn)行更新時,先去更新SoR,成功之后在更新緩存。

  1. Caffeine實(shí)現(xiàn)Write Through

Cache<string, string> cache = Caffeine.newBuilder()
        .maximumSize(100)
        .writer(new CacheWriter<string, string>() {
            @Override
            public void write(@NonNull String key, @NonNull String value) {
                //write data to database
                System.out.println(key);
                System.out.println(value);
            }

            @Override
            public void delete(@NonNull String key, @Nullable String value, @NonNull RemovalCause removalCause) {
                //delete from database
            }
        })
        .build();

cache.put("name", "silently9527");

Caffeine通過使用CacheWriter來實(shí)現(xiàn)Write Through,CacheWriter可以同步的監(jiān)聽到緩存的創(chuàng)建、變更和刪除操作,只有寫成功了才會去更新緩存

  1. EhCache實(shí)現(xiàn)Write Through

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB); //設(shè)置最大緩存條目數(shù)
CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(String.class, String.class, resource)
        .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
        .withLoaderWriter(new CacheLoaderWriter<string, string>(){
            @Override
            public String load(String key) throws Exception {
                return "silently9527";
            }

            @Override
            public void write(String key, String value) throws Exception {
                //write data to database
                System.out.println(key);
                System.out.println(value);
            }

            @Override
            public void delete(String key) throws Exception {
                //delete from database
            }
        })
        .build();

Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig);
System.out.println(cache.get("name"));

cache.put("website","https://silently9527.cn");

EhCache還是通過CacheLoaderWriter來實(shí)現(xiàn)的,當(dāng)我們調(diào)用cache.put("xxx","xxx")進(jìn)行寫緩存的時候,EhCache首先會將寫的操作委托給CacheLoaderWriter,有CacheLoaderWriter.write去負(fù)責(zé)寫數(shù)據(jù)源

Write Behind

這種模式通常先將數(shù)據(jù)寫入緩存,再異步地寫入數(shù)據(jù)庫進(jìn)行數(shù)據(jù)同步。這樣的設(shè)計既可以減少對數(shù)據(jù)庫的直接訪問,降低壓力,同時對數(shù)據(jù)庫的多次修改可以合并操作,極大地提升了系統(tǒng)的承載能力。但是這種模式也存在風(fēng)險,如當(dāng)緩存機(jī)器出現(xiàn)宕機(jī)時,數(shù)據(jù)有丟失的可能。

  1. Caffeine要想實(shí)現(xiàn)Write Behind可以在CacheLoaderWriter.write方法中把數(shù)據(jù)發(fā)送到MQ中,實(shí)現(xiàn)異步的消費(fèi),這樣可以保證數(shù)據(jù)的安全,但是要想實(shí)現(xiàn)合并操作就需要擴(kuò)展功能更強(qiáng)大的CacheLoaderWriter。

  2. EhCache實(shí)現(xiàn)Write Behind

//1 定義線程池
PooledExecutionServiceConfiguration testWriteBehind = PooledExecutionServiceConfigurationBuilder
        .newPooledExecutionServiceConfigurationBuilder()
        .pool("testWriteBehind", 5, 10)
        .build();

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
        .using(testWriteBehind)
        .build(true);
ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB); //設(shè)置最大緩存條目數(shù)

//2 設(shè)置回寫模式配置
WriteBehindConfiguration testWriteBehindConfig = WriteBehindConfigurationBuilder
        .newUnBatchedWriteBehindConfiguration()
        .queueSize(10)
        .concurrencyLevel(2)
        .useThreadPool("testWriteBehind")
        .build();

CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder
        .newCacheConfigurationBuilder(String.class, String.class, resource)
        .withLoaderWriter(new CacheLoaderWriter<string, string>() {
            @Override
            public String load(String key) throws Exception {
                return "silently9527";
            }

            @Override
            public void write(String key, String value) throws Exception {
                //write data to database
            }

            @Override
            public void delete(String key) throws Exception {
            }
        })
        .add(testWriteBehindConfig)
        .build();

Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig);

首先使用PooledExecutionServiceConfigurationBuilder定義了線程池配置;然后使用WriteBehindConfigurationBuilder設(shè)置會寫模式配置,其中newUnBatchedWriteBehindConfiguration表示不進(jìn)行批量寫操作,因?yàn)槭钱惒綄懀孕枰褜懖僮飨确湃氲疥?duì)列中,通過queueSize設(shè)置隊(duì)列大小,useThreadPool指定使用哪個線程池; concurrencyLevel設(shè)置使用多少個并發(fā)線程和隊(duì)列進(jìn)行Write Behind

EhCache實(shí)現(xiàn)批量寫的操作也很容易

  • 首先把newUnBatchedWriteBehindConfiguration()替換成newBatchedWriteBehindConfiguration(10, TimeUnit.SECONDS, 20),這里設(shè)置的是數(shù)量達(dá)到20就進(jìn)行批處理,如果10秒內(nèi)沒有達(dá)到20個也會進(jìn)行處理

  • 其次在CacheLoaderWriter中實(shí)現(xiàn)wirteAll 和 deleteAll進(jìn)行批處理

> 如果需要把對相同的key的操作合并起來只記錄最后一次數(shù)據(jù),可以通過enableCoalescing()來啟用合并

到此,相信大家對“如何理解應(yīng)用級緩存”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

分享文章:如何理解應(yīng)用級緩存
文章起源:http://m.rwnh.cn/article48/jipshp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、網(wǎng)頁設(shè)計公司、商城網(wǎng)站、品牌網(wǎng)站建設(shè)、移動網(wǎng)站建設(shè)標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都seo排名網(wǎng)站優(yōu)化
临夏市| 吴桥县| 青川县| 工布江达县| 韶关市| 正蓝旗| 容城县| 南陵县| 渝北区| 福泉市| 长宁区| 建水县| 辛集市| 西丰县| 台南市| 达州市| 葵青区| 阿勒泰市| 麻栗坡县| 仁化县| 铁岭县| 贵阳市| 花莲县| 临安市| 承德县| 玉屏| 东乡族自治县| 兴安县| 贵定县| 枣庄市| 永兴县| 宝鸡市| 田东县| 公安县| 东明县| 乳山市| 内乡县| 宜阳县| 长宁县| 信丰县| 漳州市|