摘要:序本文主要講述下緩存的模式。更新是先更新數(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ù)。
maven這個case理論上會出現(xiàn),不過,實際上出現(xiàn)的概率可能非常低,因為這個條件需要發(fā)生在讀緩存時緩存失效,而且并發(fā)著有一個寫操作。而實際上數(shù)據(jù)庫的寫操作會比讀操作慢得多,而且還要鎖表,而讀操作必需在寫操作前進(jìn)入數(shù)據(jù)庫操作,而又要晚于寫操作更新緩存,所有的這些條件都具備的概率基本并不大。
代碼復(fù)現(xiàn)com.github.ben-manes.caffeine caffeine 2.5.5 com.google.guava guava 22.0
這里使用代碼復(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 { AtomicReferencedb = 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 { AtomicReferencedb = 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
doc這里可以看到invalidate的時候,loading又重新觸發(fā)了一次,然后臟數(shù)據(jù)就清除了
緩存更新的套路
caffeine: Java 8高性能緩存庫包
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/61877.html
摘要:總結(jié)允許的緩存寫場景大部分情況,修改成本會高于增加一次,因此應(yīng)該淘汰緩存如果還在糾結(jié),總是淘汰緩存,問題也不大先操作數(shù)據(jù)庫,還是先操作緩存這里分了兩種觀點,的觀點沈老師的觀點。這里我覺得沈老師可能忽略了并發(fā)的問題,比如說以下情況一個寫請求 緩存誤用 緩存,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個部分,通常用它來降低數(shù)據(jù)庫壓力,提升系統(tǒng)整體性能,縮短訪問時間。 有架構(gòu)師說緩存是萬金油,哪里有問...
摘要:總結(jié)允許的緩存寫場景大部分情況,修改成本會高于增加一次,因此應(yīng)該淘汰緩存如果還在糾結(jié),總是淘汰緩存,問題也不大先操作數(shù)據(jù)庫,還是先操作緩存這里分了兩種觀點,的觀點沈老師的觀點。這里我覺得沈老師可能忽略了并發(fā)的問題,比如說以下情況一個寫請求 緩存誤用 緩存,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個部分,通常用它來降低數(shù)據(jù)庫壓力,提升系統(tǒng)整體性能,縮短訪問時間。 有架構(gòu)師說緩存是萬金油,哪里有問...
摘要:最近關(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ò)安全、框架通...
閱讀 2421·2021-11-18 10:02
閱讀 1935·2021-10-13 09:40
閱讀 3013·2021-09-07 10:07
閱讀 2120·2021-09-04 16:48
閱讀 1017·2019-08-30 13:18
閱讀 2463·2019-08-29 14:03
閱讀 2931·2019-08-29 12:54
閱讀 3169·2019-08-26 11:41