摘要:序本文主要講述下緩存的模式。更新是先更新數(shù)據(jù)庫(kù),成功后,讓緩存失效為什么不是寫(xiě)完數(shù)據(jù)庫(kù)后更新緩存主要是怕兩個(gè)并發(fā)的寫(xiě)操作導(dǎo)致臟數(shù)據(jù)。
序
本文主要講述下緩存的Cache Aside模式。
Cache Aside有兩個(gè)要點(diǎn):
應(yīng)用程序先從cache取數(shù)據(jù),沒(méi)有得到,則從數(shù)據(jù)庫(kù)中取數(shù)據(jù),成功后,放到緩存中。
更新是先更新數(shù)據(jù)庫(kù),成功后,讓緩存失效.為什么不是寫(xiě)完數(shù)據(jù)庫(kù)后更新緩存?主要是怕兩個(gè)并發(fā)的寫(xiě)操作導(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ù)
一個(gè)是讀操作,但是沒(méi)有命中緩存,然后就到數(shù)據(jù)庫(kù)中取數(shù)據(jù),此時(shí)來(lái)了一個(gè)寫(xiě)操作,寫(xiě)完數(shù)據(jù)庫(kù)后,讓緩存失效,然后,之前的那個(gè)讀操作再把老的數(shù)據(jù)放進(jìn)去,所以,會(huì)造成臟數(shù)據(jù)。
maven這個(gè)case理論上會(huì)出現(xiàn),不過(guò),實(shí)際上出現(xiàn)的概率可能非常低,因?yàn)檫@個(gè)條件需要發(fā)生在讀緩存時(shí)緩存失效,而且并發(fā)著有一個(gè)寫(xiě)操作。而實(shí)際上數(shù)據(jù)庫(kù)的寫(xiě)操作會(huì)比讀操作慢得多,而且還要鎖表,而讀操作必需在寫(xiě)操作前進(jìn)入數(shù)據(jù)庫(kù)操作,而又要晚于寫(xiě)操作更新緩存,所有的這些條件都具備的概率基本并不大。
代碼復(fù)現(xiàn)com.github.ben-manes.caffeine caffeine 2.5.5 com.google.guava guava 22.0
這里使用代碼復(fù)現(xiàn)一下這個(gè)臟數(shù)據(jù)場(chǎng)景。
讀操作進(jìn)來(lái),發(fā)現(xiàn)沒(méi)有cache,則觸發(fā)loading,獲取數(shù)據(jù),尚未返回
寫(xiě)操作進(jìn)來(lái),更新數(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才返回 //此時(shí)返回的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才返回 //此時(shí)返回的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的時(shí)候,loading又重新觸發(fā)了一次,然后臟數(shù)據(jù)就清除了
緩存更新的套路
caffeine: Java 8高性能緩存庫(kù)包
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/61877.html
摘要:總結(jié)允許的緩存寫(xiě)場(chǎng)景大部分情況,修改成本會(huì)高于增加一次,因此應(yīng)該淘汰緩存如果還在糾結(jié),總是淘汰緩存,問(wèn)題也不大先操作數(shù)據(jù)庫(kù),還是先操作緩存這里分了兩種觀點(diǎn),的觀點(diǎn)沈老師的觀點(diǎn)。這里我覺(jué)得沈老師可能忽略了并發(fā)的問(wèn)題,比如說(shuō)以下情況一個(gè)寫(xiě)請(qǐng)求 緩存誤用 緩存,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個(gè)部分,通常用它來(lái)降低數(shù)據(jù)庫(kù)壓力,提升系統(tǒng)整體性能,縮短訪問(wèn)時(shí)間。 有架構(gòu)師說(shuō)緩存是萬(wàn)金油,哪里有問(wèn)...
摘要:總結(jié)允許的緩存寫(xiě)場(chǎng)景大部分情況,修改成本會(huì)高于增加一次,因此應(yīng)該淘汰緩存如果還在糾結(jié),總是淘汰緩存,問(wèn)題也不大先操作數(shù)據(jù)庫(kù),還是先操作緩存這里分了兩種觀點(diǎn),的觀點(diǎn)沈老師的觀點(diǎn)。這里我覺(jué)得沈老師可能忽略了并發(fā)的問(wèn)題,比如說(shuō)以下情況一個(gè)寫(xiě)請(qǐng)求 緩存誤用 緩存,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個(gè)部分,通常用它來(lái)降低數(shù)據(jù)庫(kù)壓力,提升系統(tǒng)整體性能,縮短訪問(wèn)時(shí)間。 有架構(gòu)師說(shuō)緩存是萬(wàn)金油,哪里有問(wèn)...
摘要:最近關(guān)注的重學(xué)前端系列文章,也想把已知的前端知識(shí)體系梳理一遍,夯實(shí)基礎(chǔ)的同時(shí),總結(jié)提升。標(biāo)準(zhǔn)模式的排版和運(yùn)作模式都是以該瀏覽器支持的最高標(biāo)準(zhǔn)運(yùn)行。模式是目前最常用的模式。嚴(yán)格模式不允許展示型棄用元素和框架集。中空格不會(huì)被自動(dòng)刪除。 最近關(guān)注winter的重學(xué)前端系列文章,也想把已知的前端知識(shí)體系梳理一遍,夯實(shí)基礎(chǔ)的同時(shí),總結(jié)提升。接下來(lái)會(huì)從HTML、CSS、JS、性能、網(wǎng)絡(luò)安全、框架通...
閱讀 2406·2021-11-18 10:02
閱讀 1922·2021-10-13 09:40
閱讀 2999·2021-09-07 10:07
閱讀 2106·2021-09-04 16:48
閱讀 1005·2019-08-30 13:18
閱讀 2452·2019-08-29 14:03
閱讀 2922·2019-08-29 12:54
閱讀 3155·2019-08-26 11:41