<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          數(shù)據(jù)庫(kù)跟緩存的雙寫一致性

          共 1994字,需瀏覽 4分鐘

           ·

          2021-05-29 07:58

          1 關(guān)于一致性

          為加速系統(tǒng)性能一般都會(huì)引入緩存機(jī)制,比如 Redis。這種情況下當(dāng)用戶數(shù)據(jù)時(shí)一般會(huì)按照如下流程:

          讀流程


          關(guān)于讀的流程大家是沒有異議的,但是對(duì)于數(shù)據(jù)的更新呢,如何操作才算合理呢?


          1. 先更新數(shù)據(jù)庫(kù)再更新緩存。
          2. 先刪緩存再更新數(shù)據(jù)庫(kù)。
          3. 先更新數(shù)據(jù)庫(kù)再刪緩存。

           

          2 一致性解決方法

          2.1 緩存TTL

          簡(jiǎn)單直接又暴力的方法,如果有些數(shù)據(jù)不重要,我們讀完一次數(shù)據(jù)到緩存后設(shè)置個(gè)TTL即可,等待超時(shí)后緩存自動(dòng)從數(shù)據(jù)庫(kù)讀取下數(shù)據(jù)。

          2.2  先更新數(shù)據(jù)庫(kù) 再更新緩存

          假如我們有A、B兩個(gè)請(qǐng)求,A請(qǐng)求將age = 14,B請(qǐng)求將age = 12。我們看下正常執(zhí)行跟非正常執(zhí)行情況:
          緩存舊數(shù)據(jù)

          可發(fā)現(xiàn)如果出現(xiàn)網(wǎng)絡(luò)震蕩會(huì)導(dǎo)致緩存的數(shù)據(jù)是舊數(shù)據(jù)。因此這種方法不可取。并且如果是如下場(chǎng)景也不合適:

          1. 寫場(chǎng)景多而讀場(chǎng)景少的業(yè)務(wù)需求,此時(shí)緩存不是經(jīng)常性的讀,卻被頻繁的更新。
          2. 如果緩存的數(shù)據(jù)是經(jīng)過各種復(fù)雜計(jì)算后寫入的,那每次寫入緩存都要運(yùn)算一次,此法不可取。

          2.3 先刪緩存 再更新數(shù)據(jù)庫(kù)

          假如A先請(qǐng)求更改數(shù)據(jù),B請(qǐng)求讀數(shù)據(jù),如果因?yàn)榫W(wǎng)絡(luò)導(dǎo)致發(fā)生如下情況也會(huì)造成緩存臟數(shù)據(jù),如果此時(shí)緩存沒有設(shè)置TTL那會(huì)一直是臟數(shù)據(jù)。
          緩存臟數(shù)據(jù)

          上面這種情況如何解決呢?一般可以采用延時(shí)雙刪策略,他的核心執(zhí)行流程如下:

          public  void write(String key,Object value){
              redis.delKey(key);
              db.updateValue(value);
              Thread.sleep(1000); // 再次刪除
              redis.delKey(key);
          }

          該思路落實(shí)到流程圖上如下所示:
          延時(shí)雙刪策略

          sleep的時(shí)間要根據(jù)業(yè)務(wù)數(shù)據(jù)邏輯耗時(shí)而定,反正目的是確保讀請(qǐng)求結(jié)束,寫請(qǐng)求可以刪除讀請(qǐng)求造成的緩存臟數(shù)據(jù)
          當(dāng)然如果用的是主從寫讀架構(gòu),那處理思路跟上面類似,無(wú)非就是休眠時(shí)間再加上主從同步的時(shí)間即可。
          主從模式二次刪除

          可是其實(shí)第二次刪除還是有不妥的地方:

          1. 二次刪除前面涉及到休眠,可能導(dǎo)致系統(tǒng)性能降低,可以采用異步的方式,再起一個(gè)線程來(lái)進(jìn)行異步刪除。
          2. 如果二次刪除失敗了,還是會(huì)導(dǎo)致緩存臟數(shù)據(jù)存在的啊!

          2.4 先更新數(shù)據(jù)庫(kù) 再刪緩存

          針對(duì)緩存更新問題,老外提出了一個(gè)名為《Cache-Aside pattern》的緩存更新套路,該策略在Facebook中也廣泛使用,該策略指出:
          1. 失效:應(yīng)用程序先從緩存取數(shù)據(jù),沒有得到,則從數(shù)據(jù)庫(kù)中取數(shù)據(jù),成功后,放到緩存中。
          2. 命中:應(yīng)用程序從緩存中取數(shù)據(jù),取到后返回。
          3. 更新:先把數(shù)據(jù)存到數(shù)據(jù)庫(kù)中,成功后,再讓緩存失效。
          假如此時(shí)A、B兩個(gè)線程同時(shí)請(qǐng)求,正常來(lái)講不管你是讀寫分離還是單機(jī)版,讀一般比寫快。那刪除緩存一般是有效的。
          先更新數(shù)據(jù)庫(kù)再刪除緩存

          但是也有可能別的原因?qū)е伦x比寫還慢,導(dǎo)致我們刪了個(gè)寂寞,雖然這種情況很少發(fā)生。
          讀比寫還慢時(shí)

          該方案相比先刪除緩存再更新數(shù)據(jù)庫(kù)還是穩(wěn)妥些的,但是也不是萬(wàn)無(wú)一失的。不管是先刪緩存再更新數(shù)據(jù)庫(kù)還是先更新數(shù)據(jù)庫(kù)再刪緩存,如果刪除緩存失敗了都會(huì)導(dǎo)致緩存跟數(shù)據(jù)不一致問題

          2.5 消息隊(duì)列 確保消息刪除

          通過消息隊(duì)列的確認(rèn)消費(fèi)機(jī)制來(lái)刪除緩存。
          消息隊(duì)列機(jī)制確保刪除

          缺點(diǎn)也很明顯:
          1. 對(duì)業(yè)務(wù)線代碼造成大量的侵入,引入了中間件。
          2. 消息的延遲刪除也會(huì)造成短暫的不一致。

          2.6 專門程序+消息隊(duì)列 確保消息刪除

          該方案啟動(dòng)一個(gè)訂閱程序去訂閱數(shù)據(jù)庫(kù)的binlog,獲得需要操作的數(shù)據(jù)。在應(yīng)用程序中,另起一段程序,獲得這個(gè)訂閱程序傳來(lái)的信息,進(jìn)行刪除緩存操作。
          專門程序刪除緩存

          訂閱binlog程序在mysql中有現(xiàn)成的中間件叫canal,可以完成訂閱binlog日志的功能。

           

          3 總結(jié)

          分析后你會(huì)發(fā)現(xiàn)數(shù)據(jù)更新時(shí)緩存是刪除不是更新,而刪除緩存一般有三種方法:
          1. 如果緩存數(shù)據(jù)不敏感,直接給緩存設(shè)置TTL即可。
          2. 先刪緩存再更新數(shù)據(jù)庫(kù),此時(shí)需配合延時(shí)雙刪技術(shù),但可能導(dǎo)致二次刪除失敗。
          3. 先更新數(shù)據(jù)庫(kù)再刪緩存,此時(shí)需配合binlog消費(fèi) + 消息隊(duì)列來(lái)實(shí)現(xiàn)。


          4 參考

          1. Java后端:http://rjzheng.cnblogs.com
          2. 艾小仙分布式鎖:https://t.1yb.co/jaaA


          有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號(hào)


          好文章,我在看??

          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲视频在线免费播放 | 日日騒av无码 | 蜜桃av免费 | 污污污网站在线观看 | 成人黄色大片 |