日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

在日常的應(yīng)用開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到需要使用多種不同類型的數(shù)據(jù)庫(kù)管理系統(tǒng)來(lái)滿足各種業(yè)務(wù)需求。其中最典型的就是redis和MySQL的組合使用。

這兩者擁有各自的優(yōu)點(diǎn),例如Redis為高性能的內(nèi)存數(shù)據(jù)庫(kù)提供了極快的讀寫速度,而MySQL則是非常強(qiáng)大的關(guān)系型數(shù)據(jù)庫(kù),支持事務(wù)處理,并且提供了很好的數(shù)據(jù)一致性。

然而,在實(shí)際應(yīng)用過(guò)程中,如何保證Redis和MySQL雙寫時(shí)的數(shù)據(jù)一致性問(wèn)題成為了開(kāi)發(fā)者們面臨的重要挑戰(zhàn)。本文即將針對(duì)這個(gè)問(wèn)題進(jìn)行深入探討,希望能為廣大開(kāi)發(fā)者們提供一些有價(jià)值的思路和解決方案。

雙寫一致問(wèn)題

雙寫一致性問(wèn)題主要是指當(dāng)我們同時(shí)向Redis和MySQL寫數(shù)據(jù)時(shí),由于網(wǎng)絡(luò)延遲、服務(wù)器故障等原因,可能導(dǎo)致數(shù)據(jù)在兩個(gè)系統(tǒng)之間產(chǎn)生不一致。

例如,你可能已經(jīng)更新了MySQL中的數(shù)據(jù),但是Redis中的數(shù)據(jù)還未來(lái)得及更新,或者反過(guò)來(lái)。這樣的結(jié)果就可能導(dǎo)致用戶讀到的是舊的、不正確的數(shù)據(jù)。

比如在現(xiàn)實(shí)生活中的購(gòu)物網(wǎng)站場(chǎng)景:假設(shè)用戶A在購(gòu)買一件庫(kù)存僅剩1件的商品,系統(tǒng)在接收到請(qǐng)求后,先將MySQL中的庫(kù)存減少1,然后出現(xiàn)了網(wǎng)絡(luò)延遲或系統(tǒng)故障,Redis中的庫(kù)存沒(méi)有減少。此時(shí),用戶B看到的是還有1件商品,也發(fā)起了購(gòu)買請(qǐng)求,如果系統(tǒng)又首先更改了MySQL,那么就會(huì)出現(xiàn)超賣的情況,即實(shí)際庫(kù)存已經(jīng)沒(méi)有,但因?yàn)榫彺嬷械男畔⒉粶?zhǔn)確,導(dǎo)致系統(tǒng)銷售了更多的商品。

嚴(yán)格意義上任何非原子操作都不可能保證一致性,除非用阻塞讀寫實(shí)現(xiàn)強(qiáng)一致性,所以對(duì)于緩存架構(gòu)我們追求的目標(biāo)是最終一致性。

實(shí)際上,緩存就是通過(guò)犧牲強(qiáng)一致性來(lái)提高性能的。這是由CAP理論決定的。緩存系統(tǒng)適用的場(chǎng)景就是非強(qiáng)一致性的場(chǎng)景,它屬于CAP中的AP。

緩存讀寫策略

解決這種問(wèn)題的常見(jiàn)策略就是“緩存讀寫策略”。這個(gè)策略用于處理先更新數(shù)據(jù)庫(kù)還是先更新緩存等場(chǎng)景。

接下來(lái),我們將探討三種緩存讀寫策略。這些策略各有優(yōu)劣,沒(méi)有絕對(duì)的最佳選擇。請(qǐng)根據(jù)具體的應(yīng)用場(chǎng)景選擇最合適的策略。

Cache-Aside Pattern(旁路緩存模式)

Cache-Aside Pattern,即旁路緩存模式,它的提出是為了盡可能地解決緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致問(wèn)題。旁路緩存模式中服務(wù)端需要同時(shí)維護(hù)DBCache,并且是以DB的結(jié)果為準(zhǔn)。 :從緩存讀取數(shù)據(jù),讀到直接返回。如果讀取不到的話,從數(shù)據(jù)庫(kù)加載,寫入緩存后,再返回響應(yīng)。

:更新的時(shí)候,先「更新數(shù)據(jù)庫(kù),然后再刪除緩存」。

Read/Write Through Pattern(讀寫穿透模式)

Read/Write Through Pattern 中服務(wù)端把 cache 視為主要數(shù)據(jù)存儲(chǔ),從中讀取數(shù)據(jù)并將數(shù)據(jù)寫入其中。cache 服務(wù)負(fù)責(zé)將此數(shù)據(jù)讀取和寫入 DB,從而減輕了應(yīng)用程序的職責(zé)。

因?yàn)槲覀兘?jīng)常使用的分布式緩存 Redis 并沒(méi)有提供 cache 將數(shù)據(jù)寫入DB的功能,所以使用并不多。

:從 cache 中讀取數(shù)據(jù),讀取到就直接返回 。讀取不到的話,先從 DB 加載,寫入到 cache 后返回響應(yīng)。

從流程圖中可以看出,讀寫穿透模式和旁路緩存模式的讀取流程幾乎相同。不過(guò),在旁路緩存模式中,客戶端需要負(fù)責(zé)將數(shù)據(jù)寫入cache。而在讀寫穿透模式中,cache服務(wù)自行寫入緩存,對(duì)客戶端來(lái)說(shuō),這個(gè)過(guò)程是透明的。

:先查 cache,cache 中不存在,直接更新 DB。cache 中存在,則先更新 cache,然后 cache 服務(wù)自己更新 DB(同步更新 cache和DB)。

Write Behind Pattern(異步緩存寫入模式)

Write Behind Pattern 和 Read/Write Through Pattern 很相似,兩者都是由 cache 服務(wù)來(lái)負(fù)責(zé) cache 和 DB 的讀寫。

但是,兩個(gè)又有很大的不同:Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 則是只更新緩存,不直接更新 DB,而是改為異步批量的方式來(lái)更新 DB

很明顯,這種方式對(duì)數(shù)據(jù)一致性帶來(lái)了更大的挑戰(zhàn),比如cache數(shù)據(jù)可能還沒(méi)異步更新DB的話,cache服務(wù)可能就掛掉了,反而會(huì)帶來(lái)更大的災(zāi)難。

這種策略在我們平時(shí)開(kāi)發(fā)過(guò)程中也非常非常少見(jiàn),但是不代表它的應(yīng)用場(chǎng)景少,比如消息隊(duì)列中消息的異步寫入磁盤、MySQL 的 InnoDB Buffer Pool 機(jī)制都用到了這種策略。

Write Behind Pattern 下 DB 的寫性能非常高,非常適合一些數(shù)據(jù)經(jīng)常變化又對(duì)數(shù)據(jù)一致性要求沒(méi)那么高的場(chǎng)景,比如瀏覽量、點(diǎn)贊量等。

旁路緩存模式解析

Cache Aside Pattern 的一些疑問(wèn)

旁路緩存模式是我們平時(shí)中使用最多的,根據(jù)該模式,我們可能會(huì)有以下幾個(gè)疑問(wèn)。

為什么寫操作是刪除緩存,而不是更新緩存

:假設(shè)線程A先發(fā)起一個(gè)寫操作,第一步先更新數(shù)據(jù)庫(kù)。線程B再發(fā)起一個(gè)寫操作,緊接著也更新了數(shù)據(jù)庫(kù)。由于網(wǎng)絡(luò)等原因,線程B比線程A先更新了緩存,然后線程A更新緩存。

這時(shí)候,緩存保存的是A的數(shù)據(jù)(老數(shù)據(jù)),而數(shù)據(jù)庫(kù)保存的是B的數(shù)據(jù)(新數(shù)據(jù)),數(shù)據(jù)就不一致了,臟數(shù)據(jù)出現(xiàn)啦。如果是「刪除緩存取代更新緩存」則不會(huì)出現(xiàn)這個(gè)臟數(shù)據(jù)問(wèn)題。

實(shí)際上要寫操作的時(shí)候更新緩存也是可以的,不過(guò)我們需要加一個(gè)鎖/分布式鎖來(lái)保證更新cache的時(shí)候不存在線程安全問(wèn)題。

在寫數(shù)據(jù)的過(guò)程中,為什么要先更新DB再刪除緩存

:假設(shè)請(qǐng)求1 是寫操作,要是先刪除緩存A,這時(shí)候來(lái)了請(qǐng)求2,請(qǐng)求2是讀操作,先讀緩存A,發(fā)現(xiàn)緩存被刪除了(被請(qǐng)求1刪除了),然后去讀數(shù)據(jù)庫(kù),但是此時(shí)請(qǐng)求1還沒(méi)來(lái)得及把數(shù)據(jù)及時(shí)更新,那么請(qǐng)求2讀的就是舊數(shù)據(jù),并且請(qǐng)求2還會(huì)把讀到的舊數(shù)據(jù)放到緩存中,造成了數(shù)據(jù)的不一致。

其實(shí)要先刪緩存,再更新數(shù)據(jù)庫(kù)也是可以,如采用「延時(shí)雙刪策略」。

休眠一段時(shí)間,再次淘汰緩存。這么做,可以將這段時(shí)間內(nèi)所造成的緩存臟數(shù)據(jù),再次刪除。

注意sleep休眠的時(shí)間不能小于修改數(shù)據(jù)庫(kù)數(shù)據(jù)的時(shí)間小,基本上1秒就夠了。

在寫數(shù)據(jù)的過(guò)程中,先更新DB,后刪除cache就沒(méi)有問(wèn)題了么?

答: 理論上來(lái)說(shuō)還是可能會(huì)出現(xiàn)數(shù)據(jù)不一致性的問(wèn)題,不過(guò)概率非常小。

假設(shè)這會(huì)有兩個(gè)請(qǐng)求,一個(gè)請(qǐng)求A做查詢操作,一個(gè)請(qǐng)求B做更新操作,那么會(huì)有如下情形產(chǎn)生:

  1. 緩存剛好失效。
  2. 請(qǐng)求A查詢數(shù)據(jù)庫(kù),得一個(gè)舊值。
  3. 請(qǐng)求B將新值寫入數(shù)據(jù)庫(kù)。
  4. 請(qǐng)求B刪除緩存。
  5. 請(qǐng)求A將查到的舊值寫入緩存 ok,如果發(fā)生上述情況,確實(shí)是會(huì)發(fā)生臟數(shù)據(jù)。

然而,發(fā)生這種情況的概率并不高

發(fā)生上述情況有一個(gè)先天性條件,就是步驟(3)的寫數(shù)據(jù)庫(kù)操作比步驟(2)的讀數(shù)據(jù)庫(kù)操作耗時(shí)更短,才有可能使得步驟(4)先于步驟(5)。

可是,仔細(xì)想想,數(shù)據(jù)庫(kù)的讀操作的速度遠(yuǎn)快于寫操作的(不然做讀寫分離干嘛,做讀寫分離的意義就是因?yàn)樽x操作比較快,耗資源少),因此步驟(3)耗時(shí)比步驟(2)更短,這一情形很難出現(xiàn)。

還有其他造成不一致的原因么?

答:  如果刪除緩存過(guò)程中失敗了就會(huì)造成不一致問(wèn)題??梢允褂肅anal去訂閱數(shù)據(jù)庫(kù)的binlog,獲得需要操作的數(shù)據(jù)。另起一個(gè)程序,獲得這個(gè)訂閱程序傳來(lái)的信息,進(jìn)行刪除緩存操作。

Cache Aside Pattern 的缺陷

Cache Aside Pattern是一種常見(jiàn)的緩存更新策略,主要在讀取數(shù)據(jù)時(shí)用于處理緩存的失效和更新。盡管它有很多優(yōu)點(diǎn),但也存在一些缺陷:

缺陷1:首次請(qǐng)求數(shù)據(jù)一定不在 cache 的問(wèn)題

解決辦法:可以將熱點(diǎn)數(shù)據(jù)提前放入cache 中。

缺陷2:寫操作比較頻繁的話導(dǎo)致cache中的數(shù)據(jù)會(huì)被頻繁被刪除,這樣會(huì)影響緩存命中率 。

  • 數(shù)據(jù)庫(kù)和緩存數(shù)據(jù)強(qiáng)一致場(chǎng)景 :更新DB的時(shí)候同樣更新cache,不過(guò)我們需要加一個(gè)鎖/分布式鎖來(lái)保證更新cache的時(shí)候不存在線程安全問(wèn)題。
  • 可以短暫地允許數(shù)據(jù)庫(kù)和緩存數(shù)據(jù)不一致的場(chǎng)景 :更新DB的時(shí)候同樣更新cache,但是給緩存加一個(gè)比較短的過(guò)期時(shí)間,這樣的話就可以保證即使數(shù)據(jù)不一致的話影響也比較小。

延時(shí)雙刪

Redis的延時(shí)雙刪策略主要用于解決分布式系統(tǒng)當(dāng)中的緩存與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性問(wèn)題。以下是其基本步驟:

  1. 先刪除緩存。
  2. 再更新數(shù)據(jù)庫(kù)。
  3. 最后延時(shí)再次刪除緩存。

該策略的理念是:如果有其他線程在步驟1和步驟2之間查詢到舊的數(shù)據(jù)并寫入了緩存,那么步驟3可以保證這部分舊的數(shù)據(jù)被清除,從而盡可能維持?jǐn)?shù)據(jù)庫(kù)和緩存之間的數(shù)據(jù)一致性。

以下是使用JAVA實(shí)現(xiàn)的樣例代碼:

import redis.clients.jedis.Jedis;

public class RedisDoubleDelStrategy {
    private Jedis jedis;
    private static final long DELAY_MILLIS = 1000L; // 設(shè)置為你需要的延時(shí)時(shí)間

    public RedisDoubleDelStrategy(String host, int port) {
        this.jedis = new Jedis(host, port);
    }

    public void updateDBAndCache(String key, String value) {
        // Step 1: 刪除緩存
        jedis.del(key);

        // Step 2: 更新數(shù)據(jù)庫(kù),此處以打印輸出代替
        System.out.println("Update DB with: " + value);

        // 延遲任務(wù)來(lái)完成第二次刪除
        new Thread(() -> {
            try {
                Thread.sleep(DELAY_MILLIS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            // Step 3: 延時(shí)后再次刪除緩存
            jedis.del(key);
        }).start();
    }
}

這段代碼實(shí)現(xiàn)了延時(shí)雙刪策略,但請(qǐng)注意它仍然不能完全保證數(shù)據(jù)庫(kù)和緩存之間的一致性。

在某些情況下(比如大量并發(fā)情況下),可能仍然會(huì)出現(xiàn)不一致的問(wèn)題。例如,在步驟3之后,如果還有其他線程查詢到了舊數(shù)據(jù)并寫入了緩存,那么數(shù)據(jù)庫(kù)和緩存的數(shù)據(jù)就會(huì)不一致。因此,在使用該策略時(shí),需要根據(jù)你的系統(tǒng)特性和一致性需求來(lái)進(jìn)行權(quán)衡。

本篇文章到這就結(jié)束了,在探討Redis與MySQL雙寫問(wèn)題的過(guò)程中,我們分析了各種可能的場(chǎng)景和解決方案。雙寫系統(tǒng)不僅考驗(yàn)我們對(duì)數(shù)據(jù)庫(kù)原理的理解,也展示了協(xié)同工作的復(fù)雜性。最終,解決這個(gè)問(wèn)題的關(guān)鍵是理解你的用例并根據(jù)實(shí)際需求選擇適當(dāng)?shù)牟呗院凸ぞ摺?/p>

而在實(shí)際應(yīng)用中,再完美的方案也可能會(huì)遇到挑戰(zhàn)和困難。因此,持續(xù)監(jiān)控,頻繁測(cè)試和及時(shí)調(diào)整策略都至關(guān)重要。希望本文能為你在處理Redis與MySQL雙寫問(wèn)題上提供一些思路和靈感,同時(shí),我們也期待在未來(lái)看到更多精妙的解決方案誕生。

分享到:
標(biāo)簽:Redis
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定