談了千百遍的數據一致性

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/08/30/17666637.html
-Advertisement-
Play Games

今天來說一個老生常談的問題,來看一個實際案例:業務中往往都會通過緩存來提高查詢效率,降低資料庫的壓力,尤其是在分散式高併發場景下,大量的請求直接訪問Mysql很容易造成性能問題。 ...


今天來說一個老生常談的問題,來看一個實際案例:

現有業務中往往都會通過緩存來提高查詢效率,降低資料庫的壓力,尤其是在分散式高併發場景下,大量的請求直接訪問Mysql很容易造成性能問題。

有一天老闆找到了你......

老闆:聽說你會緩存?

你:來看我操作。

你設計了一個最常見的緩存方案,基於這種方案,開始對用戶積分功能進行優化,但當你睡的正酣時,系統悄悄進行了下麵操作:

1、線程A根據業務會把用戶id為1的積分更新成100

2、 線程B根據業務會把用戶id為1的積分更新成200

3、在資料庫層面,由於資料庫用鎖來保證了ACID,線程A和線程B不存在併發情況,,無論資料庫中最終的值是100還是200,我們都假設正確

4、假設線程B在A之後更新資料庫,則資料庫中的值為200

5、線程A和線程B在回寫緩存過程中,很可能會發生線程A線上程B之後操作緩存的情況(因為網路調用存在不確定性),這個時候緩存內的值會被更新成100,發生了緩存和資料庫不一致的情況。

第二天早上你收到了用戶投訴,怎麼辦?人工修改積分值還是刪庫跑路?

凡是處於不同物理位置的兩個操作,如果操作的是相同數據,都會遇到一致性問題,這是分散式系統不可避免的一個痛點。

1 什麼是數據一致性?

數據一致性通常講的主要是數據存儲系統,主從mysql、分散式存儲系統等,如何保證數據一致性,

比如說主從一致性,副本一致性,保證不同的時間或者相同的請求訪問這種主從資料庫時訪問的數據是一致性的,不會這次訪問是結果A下次是結果B。

2 CAP定理

說到數據一致性,就必須說CAP定理。

CAP定理是2000年由Brewer提出的,他認為分散式系統在設計和部署時,面臨3個核心問題:

Consistency:一致性。資料庫ACID操作是在一個事務中對數據加以約束,使得執行後仍處於一致狀態,而分散式系統在進行更新操作時所有的用戶都應該讀到最新值。

Availability:可用性。每一個操作總是能夠在一定時間內返回結果。結果可以是成功或失敗,一定時間是給定的時間。

Partition Tolerance:分區容忍性。考慮系統效能和可伸縮性,是否可進行數據分區。

CAP定理認為,一個提供數據服務的存儲系統無法同時滿足數據一致性、數據可用性、分區容忍性。

為什麼?如果採用分區,分散式節點之間就需要進行通信,涉及到通信,就會存在某一時刻這一節點只完成一部分業務操作,在通信完成的這一段時間內,數據就是不一致的。如果要保證一致性,就要 在通信完成的這段時間內保護數據,使得對訪問這些數據的操作都不可用。

反過來思考,如果想保證一致性和可用性,那麼數據就不能夠分區。一個簡單的理解就是所有的數據就必須存放在一個資料庫裡面,不能進行資料庫拆分。這個對於大數據量、高併發的互聯網應用來說,是不可接受的。

3 數據一致性模型

基於CAP定理,一些分散式系統通過複製數據來提高系統的可靠性和容錯性,也就是將數據的不同副本存放在不同的機器。常用的一致性模型有:

強一致性: 數據更新完成後,任何後續訪問將會返回最新的數據。這在分散式網路環境幾乎不可能實現。

弱一致性:系統不保證數據更新後的訪問會得到最新的數據。客戶端獲取最新的數據之前需要滿足一些特殊條件。

最終一致性:是弱一致性的一種特例,保證用戶最終能夠讀取到某操作對系統特定數據的更新。

4 如何保證數據一致性?

針對剛開始的問題,如果加以思考,你可能會發現不管是先寫MySQL資料庫,再刪除Redis緩存;還是先刪除緩存,再寫庫,都有可能出現數據不一致的情況。

(1)先刪除緩存

1、如果先刪除Redis緩存數據,然而還沒有來得及寫入MySQL,另一個線程就來讀取;

2、這個時候發現緩存為空,則去Mysql資料庫中讀取舊數據寫入緩存,此時緩存中為臟數據;

3、然後資料庫更新後發現Redis和Mysql出現了數據不一致的問題。

(2)後刪除緩存

1、如果先寫了庫,然後再刪除緩存,不幸的寫庫的線程掛了,導致了緩存沒有刪除;

2、這個時候就會直接讀取舊緩存,最終也導致了數據不一致情況;

3、因為寫和讀是併發的,沒法保證順序,就會出現緩存和資料庫的數據不一致的問題。

解決方案1:分散式鎖

在平時開發中,利用分散式鎖可能算是比較常見的解決方案了。利用分散式鎖把緩存操作和資料庫操作封裝為邏輯上的一個操作可以保證數據的一致性,具體流程為:

1、每個想要操作緩存和資料庫的線程都必須先申請分散式鎖;

2、如果成功獲得鎖,則進行資料庫和緩存操作,操作完畢釋放鎖;

3、如果沒有獲得鎖,根據不同業務可以選擇阻塞等待或者輪訓,或者直接返回的策略。

流程見下圖:

利用分散式鎖是解決分散式事務的一種方案,但是在一定程度上會降低系統的性能,而且分散式鎖的設計要考慮到down機和死鎖的意外情況。

解決方案2:延遲雙刪

在寫庫前後都進行redis.del(key)操作,並且設定合理的超時時間。

偽代碼如下:

public void write( String key, Object data ){
  redis.delKey( key );
  db.updateData( data );
  Thread.sleep( 500 );
  redis.delKey( key );
}



具體步驟:

1、先刪除緩存

2、再寫資料庫

3、休眠500毫秒(這個根據讀取的業務時間來定)

4、再次刪除緩存

來看之前的案例在這種方案下的情景:

T1線程線刪除緩存再更新db , T1線程更新db完成之前T2線程如果讀取到db舊的數據, 會再把舊的數據寫入Redis緩存。

此時T1線程延遲一段時間後再刪除Redis緩存操作. 當其他線程再讀取緩存為null時會查詢db最新數據重新進行緩存, 保證了Mysql和Redis緩存的數據一致性。

在此基礎上,緩存也要設置過期時間,來保證最終數據的一致性。 只要緩存過期,就去讀資料庫然後重新緩存。

這種雙刪+緩存超時的策略,最差的情況是在緩存過期時間內發生數據存在不一致,而且寫的時候增加了耗時。

但是這種方案還會出現一個問題,如何保證寫入庫後,再次刪除緩存成功?

如果刪除失敗,還有可能出現數據不一致的情況。這時候需要提供一個重試方案。

解決方案3:非同步更新緩存(基於Mysql binlog的同步機制)

1、涉及到更新的數據操作,利用Mysql binlog 進行增量訂閱消費;

2、將消息發送到消息隊列;

3、通過消息隊列消費將增量數據更新到Redis上。

這樣的效果是:

讀取Redis緩存:熱數據都在Redis上;

寫Mysql:增刪改都是在Mysql進行操作;

更新Redis數據:Mysql的數據操作都記錄到binlog,通過消息隊列及時更新到Redis上。

這樣一旦MySQL中產生了新的寫入、更新、刪除等操作,就可以把binlog相關的消息推送至Redis,Redis再根據binlog中的記錄,對Redis進行更新。

其實這種機制,很類似MySQL的主從備份機制,因為MySQL的主備也是通過binlog來實現的數據一致性。

方案2中的重試方案就可以藉助方案3,啟動一個訂閱程式訂閱資料庫的binlog,提取所需要的數據和key,另起代碼獲取這些信息。如果嘗試刪除緩存失敗,就發送消息給消息隊列,重新從消息隊列獲取數據,重試刪除操作。

參考文檔:

感謝閱讀~

作者:京東零售 李澤陽

來源:京東雲開發者社區 轉載請註明來源


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # QEMU直接從tap/tun取數據 **QEMU tap數據接收步驟:** 1. qemu從tun取數據包 2. qemu將數據包放入virtio硬體網卡。 3. qemu觸發中斷。 4. 虛擬機收到中斷,從virtio讀取數據。 **在qemu中步驟1(tap_read_packet)和步驟2 ...
  • Proxmox VE 是一個運行虛擬機和容器的平臺。 這是 基於 Debian Linux,完全開源。 最大 靈活性,我們實施了兩種虛擬化技術 - 基於內核的虛擬機 (KVM) 和基於容器的虛擬化 (LXC)。 Proxmox VE是一個企業級虛擬化平臺,該平臺集成了基於內核的虛擬機管理程式(KVM ...
  • 本文探討了進程調度的原理和演算法,並提供了全面的概述。進程調度是操作系統中的重要組成部分,用於決定進程的執行順序和分配CPU時間。我們討論了優先順序調度和時間片輪轉調度演算法。優先順序調度根據進程的優先順序確定執行順序,可以分為搶占式和非搶占式。時間片輪轉調度將CPU時間劃分為固定大小的時間片,每個進程在一個... ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230829150945972-2083299480.png) # 1. 精心設計的應用程式通常會在保持實現細節私有的同時公開公有介面,以便未來在不影響最終用戶的情況下修改設計 ...
  • 眾所周知,Mysql的事務隔離級別分為4個,分別是READ-UNCOMMITED,READ-COMMITED,REPEATABLE-READ,SERIALIZABLE,在常規資料庫概論中,前三種事務隔離級別會帶來臟讀、不可重覆讀、幻讀的問題,對應關係如下: ||臟讀|不可重覆讀|幻讀 | | | | ...
  • MySQL 到 SelectDB 的實時數據同步技術,通過 NineData 的數據複製控制台,僅需輕點滑鼠,即可輕鬆完成 MySQL 到 SelectDB 的同步任務配置。NineData 採用先進的數據同步技術,確保數據實時同步到 SelectDB,極大地降低了數據延遲,讓您的決策基於最新數據。 ...
  • 在這個燥熱的夏天,又突然收到告警,分庫分表的主鍵衝突了,這還能忍?不,堅決不能忍,必須解決掉!後面咱們慢慢道來是如何破局的,如何走了一條坎坷路…… ...
  • - 0.寫在前面 - 1.準備工作 - 1.1 準備Docker環境 - 1.2 下載源碼包 - 1.3 修改MySQL Shell源碼包 - 1.4 編譯相關軟體包 - 2.準備編譯MySQL Shell - 2.1 編譯MySQL 8.0.32 - 2.2 編譯MySQL Shell 8.0.3 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...