K:緩存數據庫雙寫數據一致性方案


對於緩存和數據庫雙寫,其存在着數據一致性的問題。對於數據一致性要求較高的業務場景,我們通常會選擇使用分布式事務(2pc、paxos等)來保證緩存與數據庫之間的數據強一致性,但分布式事務的復雜性與對資源的占用問題,使得該處理方式會造成系統性能的降低。對於數據一致性要求沒那么高的業務場景,選擇分布式事務的處理方式就會顯得不是那么必要。為此,在一般情況下,對於數據一致性要求沒那么高的業務場景,會選擇使用cache-aside-pattern方案來保證緩存與數據庫之間,數據的最終一致性,以下文章便是介紹並整理該cache-aside-pattern方案的內容。

對於緩存中的數據,我們提出三個目標

  1. 盡可能不將數據庫中的舊數據存入緩存中
  2. 允許緩存中的數據與數據庫中的數據存在一小段時間的不一致
  3. 緩存中的數據與數據庫中的數據存在數據不一致的時間應盡量短

對於緩存與數據庫的雙寫問題,無外乎“增刪改查” 這四個過程,再考慮進並發的“讀讀,寫寫,讀寫”情況,所有可能的情況組合並不多。為此,我們可以采用窮舉的方式對該方案進行說明。

查:

對於數據的查找,該方案的過程與cpu中查找數據的過程是一致的,其過程如下:

  1. 先查找緩存中的數據,當命中緩存的時候返回數據,查找結束。
  2. 當沒有命中緩存時,查找數據庫,對數據庫中查找出的相關數據進行處理,之后將相關的數據存入緩存中,查找結束。

其示意圖如下:

示意圖1

在考慮高並發查詢的情況下,對於該處理過程,如下示意圖所示。

示意圖2

當客戶端1、客戶端2、....、客戶端n查詢同一數據,且該數據不存在於緩存中時,所有的客戶端請求都會去訪問數據庫,此時會出現緩存擊穿的情況,對於緩存擊穿的相關問題及解決方案,我們留到下一篇文章再進行講解。

增:

對於新增數據的情況,我們將數據直接添加進數據庫中,且不將新增的數據加載入緩存中,其過程如下示意圖所示:

示意圖3

在考慮高並發之時,其過程如下示意圖所示:

示意圖4

通過示意圖,我們了解到,當采用該種方式新增數據時,在並發情況下,並未出現與我們的目標相違背的問題。

刪:

當需要對數據進行刪除時,我們有兩種刪除的方案可供選擇。

第一種: 先刪除緩存中的數據,再刪除數據庫中的數據

第二種: 先刪除數據庫中的數據,再刪除緩存中的數據

我們對這兩種方案分別進行分析:

先刪緩存:

對於先刪除緩存中的數據,后刪除數據庫中的數據這種方案,其示意圖如下:

示意圖5

考慮進高並發的情況,當存在讀請求和刪除數據的請求並發時,由於網絡的不可靠和延時問題,其可能出現如下示意圖所示的情況:

示意圖6

通過示意圖我們了解到,存在着緩存中緩存進已刪除的舊數據的情況,這違背了我們提出的第一個目標。

那么,先刪除數據庫中的數據的情況呢?會出現這樣的問題嗎?我們接着分析。

先刪數據庫:

對於先刪除數據庫中的數據,后刪除緩存中的數據這種方案,其示意圖如下。

示意圖7

同樣考慮進高並發的情況,當存在讀請求和刪除數據的請求並發時,其發生的情況如下示意圖所示:

示意圖8

此時,並未出現與我們的目標相違背的問題。綜上,我們在刪除數據時,應選擇先刪除數據庫中的數據再刪除緩存中的數據的這一方案。

但是該方案仍存在着問題。我們再往下思考,當刪除了緩存中的相關數據,此時來了大量讀取該數據的請求。這時,就會導致“緩存穿透”問題的出現(該問題同樣在下一篇文章中進行講解)。

改:

當需要對數據進行變更的時候,我們有三種方案可供選擇。

第一種: 先更新數據庫,后更新緩存

第二種: 先刪除緩存,后更新數據庫

第三種: 先更新數據庫,后刪除緩存

我們對這三種方案逐一進行分析。

先更新數據庫,后更新緩存:

對於先更新數據庫,后更新緩存這種方案,其示意圖如下:

示意圖9

考慮高並發的情況,當存在兩個更新操作的並發請求時,由於網絡的延時問題,其可能會出現如下這種情況:

示意圖10

由上述示意圖可知,當存在兩個更新操作時,其有將數據庫中的舊數據存入緩存中的情況,這違背了我們的第一個目標。那么,有沒有辦法解決呢?答案是有的,借助樂觀鎖的相關思想,我們可以給每個數據加上一個版本號,當數據要存入緩存時,比較一下數據的版本號即可。到目前為止,更新了數據庫中的相關數據之后,再更新緩存中的數據這個方案看起來是可以的,但是這個方案存在着一個問題,就是需要去更新緩存中的數據,更新緩存中的數據這個操作會影響系統的性能。特別是在寫多讀少,且緩存的數據不是直接從數據庫中存入,而是經過計算之后再進行緩存的業務場景中時,對系統性能造成的影響會更加明顯。為此我們可以借助“懶加載”的思想,在更新數據之時,將緩存中的數據進行刪除,等到需要用到的時候,才將數據加載進緩存中。下面我們將討論在更新時刪除緩存的兩個可能的方案。

先刪除緩存,后更新數據庫:

對於先刪除緩存,后更新數據庫的方案,其示意圖如下:

示意圖11

考慮高並發的情況,當存在讀請求和更新請求並發的情況,由於存在網絡延時,其可能出現如下示意圖中的情況:

示意圖12

由示意圖中的情況可知,在讀寫並發的情況下,其存在着將數據庫中的舊數據存入緩存中的問題。這違背了我們的第一個目標。

先更新數據庫,后刪除緩存:

對於先更新數據庫,后刪除緩存的情況,其如下示意圖所示:

示意圖13

考慮並發的情況,當存在讀請求和更新請求並發,且此時緩存中的數據恰好失效時,再加上由於網絡的延遲問題,則有可能會出現如下示意圖所示的情況。

示意圖14

由示意圖可知,當緩存中的數據恰好失效時,其是可能存在着緩存中存入數據庫中的舊數據的可能性的。但是,發生這種情況的概率是比較低的,因為讓該種情況出現的條件較為“苛刻”,需要恰好緩存中的數據失效,且要求“讀操作”慢於“寫操作”。但在通常情況下,“寫操作”是慢於“讀操作”的,因為寫操作一般會涉及到數據庫鎖的相關操作。

綜上,在對比了以上三種方案之后,對於數據更改的情況,我們采用先更新數據庫,后刪除緩存的方式。


公眾號二維碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM