欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis與MySQL數(shù)據(jù)一致性問(wèn)題的策略模式及解決方案

 更新時(shí)間:2024年07月29日 09:49:49   作者:echola_mendes  
開(kāi)發(fā)中,一般會(huì)使用Redis緩存一些常用的熱點(diǎn)數(shù)據(jù)用來(lái)減少數(shù)據(jù)庫(kù)IO,提高系統(tǒng)的吞吐量,本文將給大家介紹了Redis與MySQL數(shù)據(jù)一致性問(wèn)題的策略模式及解決方案,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下

在開(kāi)發(fā)中,一般會(huì)使用Redis緩存一些常用的熱點(diǎn)數(shù)據(jù)用來(lái)減少數(shù)據(jù)庫(kù)IO,提高系統(tǒng)的吞吐量

先了解一下分布式系統(tǒng)中的一致性概念。

強(qiáng)一致性:所有節(jié)點(diǎn)的數(shù)據(jù)必須實(shí)時(shí)同步,保證任何時(shí)候讀取到的數(shù)據(jù)都是最新的。

弱一致性:系統(tǒng)允許數(shù)據(jù)暫時(shí)不一致,但最終會(huì)達(dá)到一致?tīng)顟B(tài)。

最終一致性:數(shù)據(jù)更新后,經(jīng)過(guò)一段時(shí)間,系統(tǒng)會(huì)逐步達(dá)到一致?tīng)顟B(tài)。這個(gè)時(shí)間不固定,但在業(yè)務(wù)允許的范圍內(nèi)。

雙寫(xiě)一致性:當(dāng)數(shù)據(jù)同時(shí)存在于緩存(Redis)和數(shù)據(jù)庫(kù)(MySQL)時(shí),兩者之間數(shù)據(jù)一致

 那么容易出現(xiàn)數(shù)據(jù)一致性問(wèn)題的場(chǎng)景是:

  • 數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù),未更新緩存
  • 刪除緩存后,數(shù)據(jù)庫(kù)更新失敗

一、策略模式

緩存可以提升性能、緩解數(shù)據(jù)庫(kù)壓力,但是使用緩存也會(huì)導(dǎo)致數(shù)據(jù)不一致性的問(wèn)題。有三種經(jīng)典的緩存使用模式:

  • Cache-Aside Pattern
  • Read-Through/Write-through
  • Write-behind

1、旁路緩存模式(Cache Aside Pattern)

Cache Aside Pattern的提出是為了盡可能地解決緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致問(wèn)題

流程:

  • 讀取操作:先從緩存中讀取數(shù)據(jù),緩存命中返回結(jié)果;緩存未命中,從DB中讀取數(shù)據(jù),并將數(shù)據(jù)寫(xiě)入緩存。
  • 更新操作:先更DB,再刪除緩存中的舊數(shù)據(jù)。

在日常開(kāi)發(fā)中,一般使用了Cache Aside Pattern緩存更新策略模式,以數(shù)據(jù)庫(kù)為主,緩存為輔

public class CacheAsidePattern {
 
    private RedisService redis;
    private DatabaseService database;
 
    // 讀取操作
    public String getData(String key) {
        // 從緩存中獲取數(shù)據(jù)
        String value = redis.get(key);
        if (value == null) {
            // 緩存未命中,從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)
            value = database.get(key);
            if (value != null) {
                // 將數(shù)據(jù)寫(xiě)入緩存
                redis.set(key, value);
            }
        }
        return value;
    }
 
    // 更新操作
    public void updateData(String key, String value) {
        // 更新數(shù)據(jù)庫(kù)
        database.update(key, value);
        // 刪除緩存中的舊數(shù)據(jù)
        redis.delete(key);
    }
}

?:Cache-Aside在操作數(shù)據(jù)庫(kù)時(shí),為什么是先操作數(shù)據(jù)庫(kù)呢?為什么不先操作緩存呢?

1、先刪除緩存后,數(shù)據(jù)庫(kù)更新失敗

線程1:刪除緩存A,由于網(wǎng)絡(luò)問(wèn)題沒(méi)有操作數(shù)據(jù)庫(kù)失敗

線程2:查詢A,緩存無(wú)數(shù)據(jù),并把A寫(xiě)入緩存

線程1:網(wǎng)絡(luò)堵塞結(jié)束,修改數(shù)據(jù)庫(kù)A為B

那么此時(shí)緩存是A【舊數(shù)據(jù)】,數(shù)據(jù)庫(kù)是B【新數(shù)據(jù)】,臟數(shù)據(jù)出現(xiàn)啦?。?!

因此,Cache-Aside緩存模式,選擇了先操作數(shù)據(jù)庫(kù)而不是先操作緩存

2、先操作數(shù)據(jù)庫(kù)再刪除緩存方案

線程1:操作數(shù)據(jù)庫(kù),A更新數(shù)據(jù)為B,刪除緩存A

線程2:查詢A,緩存無(wú)數(shù)據(jù),并把B寫(xiě)入緩存

這種方案下,在數(shù)據(jù)庫(kù)更新成功后到刪除Redis緩存數(shù)據(jù)之前的這段時(shí)間中,其他線程讀取的數(shù)據(jù)都是舊數(shù)據(jù),等Redis刪除緩存后會(huì)重新從數(shù)據(jù)庫(kù)中讀取最新數(shù)據(jù)同步到Redis,這樣可以在一定程度上保證數(shù)據(jù)的最終一致性

但是在極端情況下,線程1的緩存刪除失敗,線程2讀取的也就是舊數(shù)據(jù)A,而不是新數(shù)據(jù)B了

這種方案也就是旁路緩存模式,那么Cache-Aside的優(yōu)缺點(diǎn)就是:

優(yōu)點(diǎn)

簡(jiǎn)單易懂,易于實(shí)現(xiàn)

讀性能高,因?yàn)榇蟛糠肿x操作都會(huì)命中緩存

缺點(diǎn)

更新數(shù)據(jù)庫(kù)后緩存可能還沒(méi)刪除,存在短暫的不一致

刪除緩存后,如果數(shù)據(jù)庫(kù)更新失敗,會(huì)導(dǎo)致數(shù)據(jù)不一致

?:Cache-Aside在寫(xiě)入請(qǐng)求的時(shí)候,為什么是刪除緩存而不是更新緩存呢?

線程1:操作數(shù)據(jù)庫(kù),更新數(shù)據(jù)為A,由于網(wǎng)絡(luò)問(wèn)題未更新緩存

線程2:操作數(shù)據(jù)庫(kù),更新數(shù)據(jù)為B,更新緩存為B

線程1:網(wǎng)絡(luò)堵塞結(jié)束,更新緩存為A

那么此時(shí)緩存是A【舊數(shù)據(jù)】,數(shù)據(jù)庫(kù)是B【新數(shù)據(jù)】,臟數(shù)據(jù)出現(xiàn)啦?。?!

如果是刪除緩存取代更新緩存則不會(huì)出現(xiàn)這個(gè)臟數(shù)據(jù)問(wèn)題?。?!

因此,Cache-Aside緩存模式,選擇了刪除緩存而不是更新緩存

適應(yīng)場(chǎng)景:適用于讀多寫(xiě)少的場(chǎng)景,特別是對(duì)數(shù)據(jù)一致性要求不是特別高的應(yīng)用

2、讀寫(xiě)穿透(Read-Through/Write-Through)

Read-Through:當(dāng)緩存未命中時(shí),自動(dòng)從數(shù)據(jù)庫(kù)加載數(shù)據(jù),并寫(xiě)入緩存

Write-Through:當(dāng)緩存更新時(shí),同步將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)

和旁路緩存模式很像,只有寫(xiě)操作不太一樣

public class ReadWriteThroughPattern {
 
    private RedisService redis;
    private DatabaseService database;
 
    // Read-Through
    public String readThrough(String key) {
        // 從緩存中獲取數(shù)據(jù)
        String value = redis.get(key);
        if (value == null) {
            // 緩存未命中,從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)
            value = database.get(key);
            if (value != null) {
                // 將數(shù)據(jù)寫(xiě)入緩存
                redis.set(key, value);
            }
        }
        return value;
    }
 
    // Write-Through
    public void writeThrough(String key, String value) {
        // 將數(shù)據(jù)寫(xiě)入緩存
        redis.set(key, value);
        // 同步將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)
        database.update(key, value);
    }
}

優(yōu)點(diǎn)

  • 保證了數(shù)據(jù)的強(qiáng)一致性,緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)始終同步。
  • 讀寫(xiě)操作都由緩存處理,數(shù)據(jù)庫(kù)壓力較小。

缺點(diǎn)

  • 寫(xiě)操作的延遲較高,因?yàn)?strong>每次寫(xiě)入緩存時(shí)都需要同步寫(xiě)入數(shù)據(jù)庫(kù),增加了系統(tǒng)的響應(yīng)時(shí)間
  • 實(shí)現(xiàn)復(fù)雜度較高,需要額外的緩存同步機(jī)制

適應(yīng)場(chǎng)景:適合讀多寫(xiě)多、且對(duì)數(shù)據(jù)一致性要求較高的場(chǎng)景

3、異步緩存寫(xiě)入(Write Behind)

異步緩存就是緩存更新后,異步批量寫(xiě)入數(shù)據(jù)庫(kù)。這種策略適用于可以容忍一定數(shù)據(jù)不一致的高性能場(chǎng)景

示例代碼:

public class WriteBehindPattern {
 
    private RedisService redis;
    private DatabaseService database;
    private UpdateQueue updateQueue;
 
    // 異步緩存寫(xiě)入
    public void writeBehind(String key, String value) {
        // 將數(shù)據(jù)寫(xiě)入緩存
        redis.set(key, value);
        // 異步將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)
        asyncDatabaseUpdate(key, value);
    }
 
    private void asyncDatabaseUpdate(String key, String value) {
        // 異步操作,將更新請(qǐng)求放入隊(duì)列
        updateQueue.add(new UpdateTask(key, value));
    }
}

優(yōu)點(diǎn)

寫(xiě)操作的性能非常高,因?yàn)橹恍韪戮彺?,?shù)據(jù)庫(kù)更新是異步進(jìn)行的

適用于對(duì)寫(xiě)操作性能要求較高的場(chǎng)景

缺點(diǎn)

存在數(shù)據(jù)不一致的風(fēng)險(xiǎn),緩存更新后數(shù)據(jù)庫(kù)可能還未更新。

實(shí)現(xiàn)復(fù)雜度較高,需要處理異步操作中的異常和重試

適應(yīng)場(chǎng)景:大批量數(shù)據(jù)讀取,允許短期數(shù)據(jù)不一致,寫(xiě)密集型場(chǎng)景

二、一致性解決方案

緩存系統(tǒng)適用的場(chǎng)景就是非強(qiáng)一致性的場(chǎng)景,它屬于CAP中的AP

CAP理論,指的是在一個(gè)分布式系統(tǒng)中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區(qū)容錯(cuò)性),三者不可得兼。

沒(méi)辦法做到數(shù)據(jù)庫(kù)與緩存絕對(duì)的一致性,但通過(guò)一些方案優(yōu)化處理,是可以保證弱一致性,最終一致性

1、緩存延遲雙刪

流程:

  • 先刪除緩存
  • 再更新數(shù)據(jù)庫(kù)
  • 休眠一會(huì)(比如1秒),再次刪除緩存

但休眠的時(shí)間內(nèi),可能有臟數(shù)據(jù),且第二次刪除也可能失敗,導(dǎo)致的數(shù)據(jù)不一致問(wèn)題

延遲雙刪策略只能保證最終的一致性,不能保證強(qiáng)一致性。由于對(duì)Redis的操作和Mysql的操作不是原子性操作,所以如果想保證數(shù)據(jù)的強(qiáng)一致性就需要加鎖控制,如下圖所示

加鎖之后勢(shì)必會(huì)帶來(lái)系統(tǒng)的吞吐量的下降,所以需要衡量利弊來(lái)確定是否使用加鎖

方案優(yōu)化:刪除失敗就多刪除幾次呀,保證刪除緩存成功就可以了!

所以可以引入刪除緩存重試機(jī)制

2、刪除重試機(jī)制

刪除緩存失敗,則將這些key放入到消息隊(duì)列中,消費(fèi)消息隊(duì)列的消息,獲取要?jiǎng)h除的key,重試刪除緩存操作

3、讀取biglog異步刪除緩存

重試刪除緩存機(jī)制還可以吧,就是會(huì)造成好多業(yè)務(wù)代碼入侵。

方案優(yōu)化:通過(guò)數(shù)據(jù)庫(kù)的binlog來(lái)異步淘汰key

以MySQL為例,通過(guò)canal監(jiān)聽(tīng)binlog日志感知數(shù)據(jù)的變動(dòng)后,canal客戶端執(zhí)行刪除Redis緩存數(shù)據(jù),如果緩存數(shù)據(jù)刪除失敗那么發(fā)送一條MQ消息讓canal客戶端繼續(xù)執(zhí)行刪除操作,這樣可以保證數(shù)據(jù)的最終一致性,但是這樣也增加了系統(tǒng)的復(fù)雜性

三、總結(jié)

(1)實(shí)際開(kāi)發(fā)中一般使用使用了Cache Aside Pattern緩存更新策略模式,此方案最大程度上保證了數(shù)據(jù)的一致性并且實(shí)現(xiàn)也最簡(jiǎn)單

(2)無(wú)論是先操作數(shù)據(jù)庫(kù)再刪除緩存還是先刪除緩存再操作數(shù)據(jù)庫(kù)都有可能會(huì)出現(xiàn)刪除緩存失敗的情況,所以需要加入刪除重試機(jī)制

(3)如果想要Redis和Mysql的數(shù)據(jù)強(qiáng)一致性,可以考慮使用加鎖的方式實(shí)現(xiàn)

以上就是Redis與MySQL數(shù)據(jù)一致性問(wèn)題的策略模式及解決方案的詳細(xì)內(nèi)容,更多關(guān)于Redis與MySQL數(shù)據(jù)一致性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Redis源碼環(huán)境構(gòu)建過(guò)程詳解

    Redis源碼環(huán)境構(gòu)建過(guò)程詳解

    這篇文章主要介紹了Redis源碼環(huán)境構(gòu)建過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • Redis如何部署哨兵

    Redis如何部署哨兵

    本文主要介紹了Redis如何部署哨兵,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Redis關(guān)于內(nèi)存碎片的解決方法

    Redis關(guān)于內(nèi)存碎片的解決方法

    今天生產(chǎn)機(jī)報(bào)內(nèi)存爆滿異常被叫過(guò)去查看問(wèn)題,通過(guò)各種排除最終定位到了Redis的內(nèi)存碎片的問(wèn)題,這篇博客將詳細(xì)介紹Redis內(nèi)存碎片問(wèn)題并給出最佳實(shí)踐解決此問(wèn)題,需要的朋友可以參考下
    2024-07-07
  • Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解

    Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解

    在本篇文章里小編給大家整理了一篇關(guān)于Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-12-12
  • Redis中統(tǒng)計(jì)各種數(shù)據(jù)大小的方法

    Redis中統(tǒng)計(jì)各種數(shù)據(jù)大小的方法

    這篇文章主要介紹了Redis中統(tǒng)計(jì)各種數(shù)據(jù)大小的方法,本文使用PHP實(shí)現(xiàn)統(tǒng)計(jì)Redis內(nèi)存占用比較大的鍵,需要的朋友可以參考下
    2015-03-03
  • Redis的5種數(shù)據(jù)類(lèi)型與常用命令講解

    Redis的5種數(shù)據(jù)類(lèi)型與常用命令講解

    今天小編就為大家分享一篇關(guān)于Redis的5種數(shù)據(jù)類(lèi)型與常用命令講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • redis添加key幾種方式

    redis添加key幾種方式

    本文主要介紹了redis添加key幾種方式,主要介紹了3種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • 如何基于Session實(shí)現(xiàn)短信登錄功能

    如何基于Session實(shí)現(xiàn)短信登錄功能

    對(duì)比起Cookie,Session是存儲(chǔ)在服務(wù)器端的會(huì)話,相對(duì)安全,并且不像Cookie那樣有存儲(chǔ)長(zhǎng)度限制,下面這篇文章主要給大家介紹了關(guān)于如何基于Session實(shí)現(xiàn)短信登錄功能的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • Spring Boot中使用Redis常用數(shù)據(jù)格式API操作技巧

    Spring Boot中使用Redis常用數(shù)據(jù)格式API操作技巧

    本文介紹了在Spring Boot中使用Redis的一些技巧和數(shù)據(jù)格式,通過(guò)配置Redis連接,可以連接到Redis數(shù)據(jù)庫(kù),結(jié)合實(shí)例代碼介紹的非常詳細(xì),需要的朋友參考下吧
    2024-03-03
  • Centos 7 如何安裝Redis(推薦)

    Centos 7 如何安裝Redis(推薦)

    這篇文章主要介紹了Centos 7 如何安裝Redis,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12

最新評(píng)論