小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

如何保證 Redis 緩存與數(shù)據(jù)庫雙寫一致性?

 路人甲Java 2021-05-14

作者:不學(xué)無數(shù)的程序員

在做系統(tǒng)優(yōu)化時(shí),想到了將數(shù)據(jù)進(jìn)行分級存儲的思路。因?yàn)樵谙到y(tǒng)中會存在一些數(shù)據(jù),有些數(shù)據(jù)的實(shí)時(shí)性要求不高,比如一些配置信息。

基本上配置了很久才會變一次。而有一些數(shù)據(jù)實(shí)時(shí)性要求非常高,比如訂單和流水的數(shù)據(jù)。所以這里根據(jù)數(shù)據(jù)要求實(shí)時(shí)性不同將數(shù)據(jù)分為三級。

  • 第1級:訂單數(shù)據(jù)和支付流水?dāng)?shù)據(jù);這兩塊數(shù)據(jù)對實(shí)時(shí)性和精確性要求很高,所以不添加任何緩存,讀寫操作將直接操作數(shù)據(jù)庫。

  • 第2級:用戶相關(guān)數(shù)據(jù);這些數(shù)據(jù)和用戶相關(guān),具有讀多寫少的特征,所以我們使用redis進(jìn)行緩存。

  • 第3級:支付配置信息;這些數(shù)據(jù)和用戶無關(guān),具有數(shù)據(jù)量小,頻繁讀,幾乎不修改的特征,所以我們使用本地內(nèi)存進(jìn)行緩存。

但是只要使用到緩存,無論是本地內(nèi)存做緩存還是使用 redis 做緩存,那么就會存在數(shù)據(jù)同步的問題,因?yàn)榕渲眯畔⒕彺嬖趦?nèi)存中,而內(nèi)存時(shí)無法感知到數(shù)據(jù)在數(shù)據(jù)庫的修改。這樣就會造成數(shù)據(jù)庫中的數(shù)據(jù)與緩存中數(shù)據(jù)不一致的問題。

接下來就討論一下關(guān)于保證緩存和數(shù)據(jù)庫雙寫時(shí)的數(shù)據(jù)一致性。

解決方案

那么我們這里列出來所有策略,并且討論他們優(yōu)劣性。

  1. 先更新數(shù)據(jù)庫,后更新緩存

  2. 先更新數(shù)據(jù)庫,后刪除緩存

  3. 先更新緩存,后更新數(shù)據(jù)庫

  4. 先刪除緩存,后更新數(shù)據(jù)庫

先更新數(shù)據(jù)庫,后更新緩存

這種場景一般是沒有人使用的,主要原因是在更新緩存那一步,為什么呢?因?yàn)橛械臉I(yè)務(wù)需求緩存中存在的值并不是直接從數(shù)據(jù)庫中查出來的,有的是需要經(jīng)過一系列計(jì)算來的緩存值,那么這時(shí)候后你要更新緩存的話其實(shí)代價(jià)是很高的。如果此時(shí)有大量的對數(shù)據(jù)庫進(jìn)行寫數(shù)據(jù)的請求,但是讀請求并不多,那么此時(shí)如果每次寫請求都更新一下緩存,那么性能損耗是非常大的。

舉個(gè)例子比如在數(shù)據(jù)庫中有一個(gè)值為 1 的值,此時(shí)我們有 10 個(gè)請求對其每次加一的操作,但是這期間并沒有讀操作進(jìn)來,如果用了先更新數(shù)據(jù)庫的辦法,那么此時(shí)就會有十個(gè)請求對緩存進(jìn)行更新,會有大量的冷數(shù)據(jù)產(chǎn)生,如果我們不更新緩存而是刪除緩存,那么在有讀請求來的時(shí)候那么就會只更新緩存一次。

先更新緩存,后更新數(shù)據(jù)庫

這一種情況應(yīng)該不需要我們考慮了吧,和第一種情況是一樣的。

先刪除緩存,后更新數(shù)據(jù)庫

該方案也會出問題,具體出現(xiàn)的原因如下。

先刪除緩存,后更新數(shù)據(jù)庫

此時(shí)來了兩個(gè)請求,請求 A(更新操作) 和請求 B(查詢操作)

  1. 請求 A 會先刪除 Redis 中的數(shù)據(jù),然后去數(shù)據(jù)庫進(jìn)行更新操作

  2. 此時(shí)請求 B 看到 Redis 中的數(shù)據(jù)時(shí)空的,會去數(shù)據(jù)庫中查詢該值,補(bǔ)錄到 Redis 中

  3. 但是此時(shí)請求 A 并沒有更新成功,或者事務(wù)還未提交

那么這時(shí)候就會產(chǎn)生數(shù)據(jù)庫和 Redis 數(shù)據(jù)不一致的問題。如何解決呢?其實(shí)最簡單的解決辦法就是延時(shí)雙刪的策略。

延時(shí)雙刪

但是上述的保證事務(wù)提交完以后再進(jìn)行刪除緩存還有一個(gè)問題,就是如果你使用的是 Mysql 的讀寫分離的架構(gòu)的話,那么其實(shí)主從同步之間也會有時(shí)間差。

主從同步時(shí)間差

此時(shí)來了兩個(gè)請求,請求 A(更新操作) 和請求 B(查詢操作)

  1. 請求 A 更新操作,刪除了 Redis

  2. 請求主庫進(jìn)行更新操作,主庫與從庫進(jìn)行同步數(shù)據(jù)的操作

  3. 請 B 查詢操作,發(fā)現(xiàn) Redis 中沒有數(shù)據(jù)

  4. 去從庫中拿去數(shù)據(jù)

  5. 此時(shí)同步數(shù)據(jù)還未完成,拿到的數(shù)據(jù)是舊數(shù)據(jù)

此時(shí)的解決辦法就是如果是對 Redis 進(jìn)行填充數(shù)據(jù)的查詢數(shù)據(jù)庫操作,那么就強(qiáng)制將其指向主庫進(jìn)行查詢。

從主庫中拿數(shù)據(jù)

先更新數(shù)據(jù)庫,后刪除緩存

問題:這一種情況也會出現(xiàn)問題,比如更新數(shù)據(jù)庫成功了,但是在刪除緩存的階段出錯(cuò)了沒有刪除成功,那么此時(shí)再讀取緩存的時(shí)候每次都是錯(cuò)誤的數(shù)據(jù)了。

先更新數(shù)據(jù)庫,后刪除緩存

此時(shí)解決方案就是利用消息隊(duì)列進(jìn)行刪除的補(bǔ)償。具體的業(yè)務(wù)邏輯用語言描述如下:

  1. 請求 A 先對數(shù)據(jù)庫進(jìn)行更新操作

  2. 在對 Redis 進(jìn)行刪除操作的時(shí)候發(fā)現(xiàn)報(bào)錯(cuò),刪除失敗

  3. 此時(shí)將Redis 的 key 作為消息體發(fā)送到消息隊(duì)列中

  4. 系統(tǒng)接收到消息隊(duì)列發(fā)送的消息后再次對 Redis 進(jìn)行刪除操作

但是這個(gè)方案會有一個(gè)缺點(diǎn)就是會對業(yè)務(wù)代碼造成大量的侵入,深深的耦合在一起,所以這時(shí)會有一個(gè)優(yōu)化的方案,我們知道對 Mysql 數(shù)據(jù)庫更新操作后再 binlog 日志中我們都能夠找到相應(yīng)的操作,那么我們可以訂閱 Mysql 數(shù)據(jù)庫的 binlog 日志對緩存進(jìn)行操作。

總結(jié)

每種方案各有利弊,比如在第二種先刪除緩存,后更新數(shù)據(jù)庫這個(gè)方案我們最后討論了要更新 Redis 的時(shí)候強(qiáng)制走主庫查詢就能解決問題,那么這樣的操作會對業(yè)務(wù)代碼進(jìn)行大量的侵入,但是不需要增加的系統(tǒng),不需要增加整體的服務(wù)的復(fù)雜度。

最后一種方案我們最后討論了利用訂閱 binlog 日志進(jìn)行搭建獨(dú)立系統(tǒng)操作 Redis,這樣的缺點(diǎn)其實(shí)就是增加了系統(tǒng)復(fù)雜度。其實(shí)每一次的選擇都需要我們對于我們的業(yè)務(wù)進(jìn)行評估來選擇,沒有一種技術(shù)是對于所有業(yè)務(wù)都通用的。沒有最好的,只有最適合我們的。



覺得不錯(cuò),別忘了隨手點(diǎn)贊+轉(zhuǎn)發(fā)哦!

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多