成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

緩存的Cache Aside模式

paulli3 / 2530人閱讀

摘要:序本文主要講述下緩存的模式。更新是先更新數(shù)據(jù)庫,成功后,讓緩存失效為什么不是寫完數(shù)據(jù)庫后更新緩存主要是怕兩個并發(fā)的寫操作導(dǎo)致臟數(shù)據(jù)。

本文主要講述下緩存的Cache Aside模式。

Cache Aside

有兩個要點:

應(yīng)用程序先從cache取數(shù)據(jù),沒有得到,則從數(shù)據(jù)庫中取數(shù)據(jù),成功后,放到緩存中。

更新是先更新數(shù)據(jù)庫,成功后,讓緩存失效.為什么不是寫完數(shù)據(jù)庫后更新緩存?主要是怕兩個并發(fā)的寫操作導(dǎo)致臟數(shù)據(jù)。

public V read(K key) {
  V result = cache.getIfPresent(key);
  if (result == null) {
    result = readFromDatabase(key);
    cache.put(key, result);
  }

  return result;
}

public void write(K key, V value) {
  writeToDatabase(key, value);
  cache.invalidate(key);
};
臟數(shù)據(jù)

一個是讀操作,但是沒有命中緩存,然后就到數(shù)據(jù)庫中取數(shù)據(jù),此時來了一個寫操作,寫完數(shù)據(jù)庫后,讓緩存失效,然后,之前的那個讀操作再把老的數(shù)據(jù)放進(jìn)去,所以,會造成臟數(shù)據(jù)。

這個case理論上會出現(xiàn),不過,實際上出現(xiàn)的概率可能非常低,因為這個條件需要發(fā)生在讀緩存時緩存失效,而且并發(fā)著有一個寫操作。而實際上數(shù)據(jù)庫的寫操作會比讀操作慢得多,而且還要鎖表,而讀操作必需在寫操作前進(jìn)入數(shù)據(jù)庫操作,而又要晚于寫操作更新緩存,所有的這些條件都具備的概率基本并不大。

maven
        
        
            com.github.ben-manes.caffeine
            caffeine
            2.5.5
        
        
        
            com.google.guava
            guava
            22.0
        
代碼復(fù)現(xiàn)

這里使用代碼復(fù)現(xiàn)一下這個臟數(shù)據(jù)場景。

讀操作進(jìn)來,發(fā)現(xiàn)沒有cache,則觸發(fā)loading,獲取數(shù)據(jù),尚未返回

寫操作進(jìn)來,更新數(shù)據(jù)源,invalidate緩存

loading獲取的舊數(shù)據(jù)返回,cache里頭存的是臟數(shù)據(jù)

@Test
    public void testCacheDirty() throws InterruptedException, ExecutionException {
        AtomicReference db = new AtomicReference<>(1);

        LoadingCache cache = CacheBuilder.newBuilder()
                .build(
                new CacheLoader() {
                    public Integer load(String key) throws InterruptedException {
                        LOGGER.info("loading reading from db ...");
                        Integer v = db.get();
                        LOGGER.info("loading read from db get:{}",v);
                        Thread.sleep(1000L); //這里1秒才返回,模擬引發(fā)臟緩存
                        LOGGER.info("loading Read from db return : {}",v);
                        return v;
                    }
                }
        );

        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LOGGER.info("Writing to db ...");
            db.set(2);
            LOGGER.info("Wrote to db");
            cache.invalidate("k");
            LOGGER.info("Invalidated cached");
        });

        t2.start();

        //這里在t2 invalidate 之前 先觸發(fā)cache loading
        //loading那里增加sleep,確保在invalidate之后,cache loading才返回
        //此時返回的cache就是臟數(shù)據(jù)了
        LOGGER.info("fire loading cache");
        LOGGER.info("get from cache: {}",cache.get("k"));

        t2.join();

        for(int i=0;i<3;i++){
            LOGGER.info("get from cache: {}",cache.get("k"));
        }
    }

輸出

15:54:05.751 [main] INFO com.example.demo.CacheTest - fire loading cache
15:54:05.772 [main] INFO com.example.demo.CacheTest - loading reading from db ...
15:54:05.772 [main] INFO com.example.demo.CacheTest - loading read from db get:1
15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Writing to db ...
15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Wrote to db
15:54:06.253 [Thread-1] INFO com.example.demo.CacheTest - Invalidated cached
15:54:06.778 [main] INFO com.example.demo.CacheTest - loading Read from db return : 1
15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
15:54:06.782 [main] INFO com.example.demo.CacheTest - get from cache: 1
使用caffeine
@Test
    public void testCacheDirty() throws InterruptedException, ExecutionException {
        AtomicReference db = new AtomicReference<>(1);

        com.github.benmanes.caffeine.cache.LoadingCache cache = Caffeine.newBuilder()
                .build(key -> {
                    LOGGER.info("loading reading from db ...");
                    Integer v = db.get();
                    LOGGER.info("loading read from db get:{}",v);
                    Thread.sleep(1000L); //這里1秒才返回,模擬引發(fā)臟緩存
                    LOGGER.info("loading Read from db return : {}",v);
                    return v;
                });

        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LOGGER.info("Writing to db ...");
            db.set(2);
            LOGGER.info("Wrote to db");
            cache.invalidate("k");
            LOGGER.info("Invalidated cached");
        });

        t2.start();

        //這里在t2 invalidate 之前 先觸發(fā)cache loading
        //loading那里增加sleep,確保在invalidate之后,cache loading才返回
        //此時返回的cache就是臟數(shù)據(jù)了
        LOGGER.info("fire loading cache");
        LOGGER.info("get from cache: {}",cache.get("k"));

        t2.join();

        for(int i=0;i<3;i++){
            LOGGER.info("get from cache: {}",cache.get("k"));
        }
    }

輸出

16:05:10.141 [main] INFO com.example.demo.CacheTest - fire loading cache
16:05:10.153 [main] INFO com.example.demo.CacheTest - loading reading from db ...
16:05:10.153 [main] INFO com.example.demo.CacheTest - loading read from db get:1
16:05:10.634 [Thread-1] INFO com.example.demo.CacheTest - Writing to db ...
16:05:10.635 [Thread-1] INFO com.example.demo.CacheTest - Wrote to db
16:05:11.172 [main] INFO com.example.demo.CacheTest - loading Read from db return : 1
16:05:11.172 [main] INFO com.example.demo.CacheTest - get from cache: 1
16:05:11.172 [Thread-1] INFO com.example.demo.CacheTest - Invalidated cached
16:05:11.172 [main] INFO com.example.demo.CacheTest - loading reading from db ...
16:05:11.172 [main] INFO com.example.demo.CacheTest - loading read from db get:2
16:05:12.177 [main] INFO com.example.demo.CacheTest - loading Read from db return : 2
16:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 2
16:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 2
16:05:12.177 [main] INFO com.example.demo.CacheTest - get from cache: 2

這里可以看到invalidate的時候,loading又重新觸發(fā)了一次,然后臟數(shù)據(jù)就清除了

doc

緩存更新的套路

caffeine: Java 8高性能緩存庫包

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/61877.html

相關(guān)文章

  • (討論)緩存同步、如何保證緩存一致性、緩存誤用

    摘要:總結(jié)允許的緩存寫場景大部分情況,修改成本會高于增加一次,因此應(yīng)該淘汰緩存如果還在糾結(jié),總是淘汰緩存,問題也不大先操作數(shù)據(jù)庫,還是先操作緩存這里分了兩種觀點,的觀點沈老師的觀點。這里我覺得沈老師可能忽略了并發(fā)的問題,比如說以下情況一個寫請求 緩存誤用 緩存,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個部分,通常用它來降低數(shù)據(jù)庫壓力,提升系統(tǒng)整體性能,縮短訪問時間。 有架構(gòu)師說緩存是萬金油,哪里有問...

    msup 評論0 收藏0
  • (討論)緩存同步、如何保證緩存一致性、緩存誤用

    摘要:總結(jié)允許的緩存寫場景大部分情況,修改成本會高于增加一次,因此應(yīng)該淘汰緩存如果還在糾結(jié),總是淘汰緩存,問題也不大先操作數(shù)據(jù)庫,還是先操作緩存這里分了兩種觀點,的觀點沈老師的觀點。這里我覺得沈老師可能忽略了并發(fā)的問題,比如說以下情況一個寫請求 緩存誤用 緩存,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個部分,通常用它來降低數(shù)據(jù)庫壓力,提升系統(tǒng)整體性能,縮短訪問時間。 有架構(gòu)師說緩存是萬金油,哪里有問...

    y1chuan 評論0 收藏0
  • 知識整理之HTML篇

    摘要:最近關(guān)注的重學(xué)前端系列文章,也想把已知的前端知識體系梳理一遍,夯實基礎(chǔ)的同時,總結(jié)提升。標(biāo)準(zhǔn)模式的排版和運(yùn)作模式都是以該瀏覽器支持的最高標(biāo)準(zhǔn)運(yùn)行。模式是目前最常用的模式。嚴(yán)格模式不允許展示型棄用元素和框架集。中空格不會被自動刪除。 最近關(guān)注winter的重學(xué)前端系列文章,也想把已知的前端知識體系梳理一遍,夯實基礎(chǔ)的同時,總結(jié)提升。接下來會從HTML、CSS、JS、性能、網(wǎng)絡(luò)安全、框架通...

    yck 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<