目錄
正文
快取延時雙刪
刪除快取重試機制
讀取biglog非同步刪除快取
首頁 資料庫 mysql教程 Redis、MySQL快取雙寫不一致怎麼辦?解決方案分享

Redis、MySQL快取雙寫不一致怎麼辦?解決方案分享

Mar 15, 2022 am 11:18 AM
mysql redis

Redis、MySQL快取雙寫不一致怎麼辦?本篇文章就來跟大家分享解決快取雙寫不一致問題的方法,希望能夠給大家幫忙!

Redis、MySQL快取雙寫不一致怎麼辦?解決方案分享

redis、mysql雙寫快取不一致:

Redis、MySQL快取雙寫不一致怎麼辦?解決方案分享

但是在更新快取方面,對於更新完資料庫,是更新快取呢,還是刪除快取。 又或是先刪除緩存,再更新資料庫,其實大家存在很大的爭議。 目前沒有一篇全面的博客,對這幾種方案進行解析。於是部落客戰戰兢兢,頂著被大家噴的風險,寫了這篇文章。

正文

給快取資料設定過期時間

先做一個說明,從理論上來說,給快取設置過期時間,是確保最終一致性的解決方案。這種方案下,我們可以對存入快取的資料設定過期時間,所有的寫入操作以資料庫為準,對快取操作只是盡力即可。也就是說如果資料庫寫成功,快取更新失敗,那麼只要到達過期時間,則後面的讀取請求自然會從資料庫讀取新值然後回填快取。因此,接下來討論的思路不依賴給快取設定過期時間這個方案。

在這裡,我們先討論三種更新策略

  1. 先更新資料庫,再更新快取
  2. 先刪除緩存,再更新資料庫
  3. 先更新資料庫,再刪除快取

先更新資料庫,再更新快取

這套方案,大家普遍反對。為什麼呢?有以下兩點:

  • 原因一(線程安全角度)

(1)線程A更新了資料庫
(2)線程B更新了資料庫
(3)線程B更新了快取
(4)線程A更新了快取

這就出現請求A更新快取應該比請求B更新快取早才對,但是因為網路等原因, B卻比A更早更新了快取。這就導致了髒數據,因此不考慮。

  • 原因二(業務場景角度)

(1)如果你是一個寫資料庫場景比較多,而讀資料場景比較少的業務需求,採用這種方案就會導致,資料壓根還沒讀到,快取就被頻繁的更新,浪費效能。

(2)如果你寫入資料庫的值,並不是直接寫入快取的,而是要經過一連串複雜的計算再寫入快取。那麼,每次寫入資料庫後,都再次計算寫入快取的值,無疑是浪費效能的。顯然,刪除快取更適合。

先刪除緩存,再更新資料庫

該方案會導致不一致原因是。同時一個請求A進行更新操作,另一個請求B進行查詢操作。那麼就會出現如下情形:

(1)請求A進行寫入操作,刪除快取
(2)請求B查詢發現快取不存在
(3)請求B去資料庫查詢得到舊值
(4)請B將舊值寫入快取
(5)請求A將新值寫入資料庫

上述情況就會導致不一致的情形出現。而且,如果不採用給快取設定過期時間策略,該資料永遠都是髒資料。

那麼,要如何解決呢?採用延時雙刪策略

快取延時雙刪

public class CacheServiceImpl implements ICacheService {

    @Resource
    private RedisOperator redisOperator;
    
    @Autowired
    private IShopService shopService;

    //1. 采用延时双删,解决数据库和缓存的一致性
    @Override
    public void updateHotCount(String id) {
        try {
            //删除缓存
            redisOperator.del("redis_key_" + id);
            //更新数据库
            shopService.updataHotShop();
            Thread.sleep(1000);//休眠1秒
            //延时删除
            redisOperator.del("redis_key_" + id);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    @Override
    public Integer getHotCount(String id) {
        return null;
    }}
登入後複製

解釋:

    ##先淘汰快取
  1. 再寫資料庫
  2. 休眠1秒,再淘汰快取(
  3. 這麼做,可以將1秒內所造成的快取髒數據,再刪除。

#針對上面的情形,讀者應該自行評估自己的專案的讀取資料業務邏輯的耗時。然後寫資料的休眠時間則在讀取資料業務邏輯的耗時基礎上,加幾百ms即可。這麼做的目的,就是確保讀取請求結束,寫入請求可以刪除讀取請求造成的快取髒資料。

如果資料庫採用了讀寫分離架構,那麼辦? 主庫負責寫入操作,從庫負責讀取操作

ok,在這種情況下,造成資料不一致的原因如下,還是兩個請求,一個請求A進行更新操作,另一個請B進行查詢操作。

(1)請求A進行寫入操作,刪除緩存,請求A把資料寫入主庫,還沒開始同步從庫

(2)(1s內)請求B查詢緩存,沒有發現緩存,請求B去從庫查詢,這時還沒有完成主從同步,查到是舊值,並且把舊值寫入緩存。

(3)主庫完成主從同步,從庫變成新值

上述流程,就是資料不一致問題,也使用雙刪延時策略。只是,睡眠時間修改為在主從同步的延時時間基礎之上,加幾百ms

採用這種同步淘汰策略,吞吐量降低怎麼辦?

ok,那就將第二次刪除當作非同步的。自己起一個線程,非同步刪除。這樣,寫的請求就不用沉睡一段時間後了,再回來。這麼做,加大吞吐量。

第二次刪除,如果刪除失敗怎麼辦?

這是個非常好的問題,因為第二次刪除失敗,就會出現如下情形。還是有兩個請求,一個請求A進行更新操作,另一個請求B進行查詢操作,為了方便,假設是單庫:

(1)請求A進行寫入操作,刪除快取
( 2)請求B查詢發現快取不存在
(3)請求B去資料庫查詢得到舊值
(4)請求B將舊值寫入快取
(5)請求A將新值寫入資料庫
(6)請求A試圖去刪除請求B寫入對快取值,結果失敗了。

ok,這也就是說。如果第二次刪除快取失敗,會再次出現快取和資料庫不一致的問題。

如何解決呢?

具體解決方案,且看部落客對第先更新資料庫,再刪除快取種更新策略的解析。

刪除快取重試機制

不管是延時雙刪還是Cache-Aside的先操作資料庫再刪除快取,都可能存在第二步驟的刪除快取失敗,導致的資料不一致問題。可以使用這個方案優化:刪除失敗就多刪除幾次呀,保證刪除快取成功就可以了呀~ 所以可以引入刪除快取重試機制

Redis、MySQL快取雙寫不一致怎麼辦?解決方案分享

  1. #(1)更新資料庫資料;
    (2)快取因為種種問題刪除失敗
    (3)將需要刪除的key傳送至訊息佇列
    (4)自己消費訊息,取得需要刪除的key
    (5)繼續重試刪除操作,直到成功

然而,該方案有一個缺點,對業務線程式碼造成大量的侵入。於是有了方案二,在方案二中,啟動一個訂閱程式去訂閱資料庫的binlog,取得需要操作的資料。在應用程式中,另起一段程序,獲得這個訂閱程序傳來的信息,進行刪除快取操作。

讀取biglog非同步刪除快取

Redis、MySQL快取雙寫不一致怎麼辦?解決方案分享

#流程如下圖所示:

(1)更新資料庫資料
(2)資料庫會將操作資訊寫入binlog日誌當中
(3)訂閱程式擷取所需的資料以及key
(4)另起一段非業務程式碼,取得該資訊
(5)嘗試刪除快取操作,發現刪除失敗
(6)將這些資訊傳送至訊息佇列
(7)重新從訊息佇列中取得該數據,重試操作。

備註說明:上述的訂閱binlog程式在mysql中有現成的中間件**叫canal,**可以完成訂閱binlog日誌的功能。至於oracle中,部落客目前不知道有沒有現成中間件可以使用。另外,重試機制,部落客是採用的是訊息佇列的方式。如果對一致性要求不是很高,直接在程式中另起一個線程,每隔一段時間去重試即可,這些大家可以靈活自由發揮,只是提供一個思路。

本文其實是對目前網路中已有的一致性方案,做了一個總結。對於先刪緩存,再更新資料庫的更新策略,還有方案提出維護一個內存隊列的方式,博主看了一下,覺得實現異常複雜,沒有必要,因此沒有必要在文中給出。最後,希望大家有所收穫。

【相關推薦:mysql影片教學

#

以上是Redis、MySQL快取雙寫不一致怎麼辦?解決方案分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

redis集群模式怎麼搭建 redis集群模式怎麼搭建 Apr 10, 2025 pm 10:15 PM

Redis集群模式通過分片將Redis實例部署到多個服務器,提高可擴展性和可用性。搭建步驟如下:創建奇數個Redis實例,端口不同;創建3個sentinel實例,監控Redis實例並進行故障轉移;配置sentinel配置文件,添加監控Redis實例信息和故障轉移設置;配置Redis實例配置文件,啟用集群模式並指定集群信息文件路徑;創建nodes.conf文件,包含各Redis實例的信息;啟動集群,執行create命令創建集群並指定副本數量;登錄集群執行CLUSTER INFO命令驗證集群狀態;使

phpmyadmin怎麼打開 phpmyadmin怎麼打開 Apr 10, 2025 pm 10:51 PM

可以通過以下步驟打開 phpMyAdmin:1. 登錄網站控制面板;2. 找到並點擊 phpMyAdmin 圖標;3. 輸入 MySQL 憑據;4. 點擊 "登錄"。

redis怎麼讀源碼 redis怎麼讀源碼 Apr 10, 2025 pm 08:27 PM

理解 Redis 源碼的最佳方法是逐步進行:熟悉 Redis 基礎知識。選擇一個特定的模塊或功能作為起點。從模塊或功能的入口點開始,逐行查看代碼。通過函數調用鏈查看代碼。熟悉 Redis 使用的底層數據結構。識別 Redis 使用的算法。

redis怎麼讀取隊列 redis怎麼讀取隊列 Apr 10, 2025 pm 10:12 PM

要從 Redis 讀取隊列,需要獲取隊列名稱、使用 LPOP 命令讀取元素,並處理空隊列。具體步驟如下:獲取隊列名稱:以 "queue:" 前綴命名,如 "queue:my-queue"。使用 LPOP 命令:從隊列頭部彈出元素並返回其值,如 LPOP queue:my-queue。處理空隊列:如果隊列為空,LPOP 返回 nil,可先檢查隊列是否存在再讀取元素。

phpmyadmin連接mysql phpmyadmin連接mysql Apr 10, 2025 pm 10:57 PM

如何使用 phpMyAdmin 連接到 MySQL?訪問 phpMyAdmin 的 URL,通常為 http://localhost/phpmyadmin 或 http://[您的服務器 IP 地址]/phpmyadmin。輸入您的 MySQL 用戶名和密碼。選擇您要連接的數據庫。點擊 "連接" 按鈕以建立連接。

redis計數器怎麼實現 redis計數器怎麼實現 Apr 10, 2025 pm 10:21 PM

Redis計數器是一種使用Redis鍵值對存儲來實現計數操作的機制,包含以下步驟:創建計數器鍵、增加計數、減少計數、重置計數和獲取計數。 Redis計數器的優勢包括速度快、高並發、持久性和簡單易用。它可用於用戶訪問計數、實時指標跟踪、遊戲分數和排名以及訂單處理計數等場景。

redis指令怎麼用 redis指令怎麼用 Apr 10, 2025 pm 08:45 PM

使用 Redis 指令需要以下步驟:打開 Redis 客戶端。輸入指令(動詞 鍵 值)。提供所需參數(因指令而異)。按 Enter 執行指令。 Redis 返迴響應,指示操作結果(通常為 OK 或 -ERR)。

redis怎麼啟動服務器 redis怎麼啟動服務器 Apr 10, 2025 pm 08:12 PM

啟動 Redis 服務器的步驟包括:根據操作系統安裝 Redis。通過 redis-server(Linux/macOS)或 redis-server.exe(Windows)啟動 Redis 服務。使用 redis-cli ping(Linux/macOS)或 redis-cli.exe ping(Windows)命令檢查服務狀態。使用 Redis 客戶端,如 redis-cli、Python 或 Node.js,訪問服務器。

See all articles