對於緩存和數據庫雙寫,其存在着數據一致性的問題。對於數據一致性要求較高的業務場景,咱們一般會選擇使用分佈式事務(2pc、paxos等)來保證緩存與數據庫之間的數據強一致性,但分佈式事務的複雜性與對資源的佔用問題,使得該處理方式會形成系統性能的下降。對於數據一致性要求沒那麼高的業務場景,選擇分佈式事務的處理方式就會顯得不是那麼必要。爲此,在通常狀況下,對於數據一致性要求沒那麼高的業務場景,會選擇使用cache-aside-pattern方案來保證緩存與數據庫之間,數據的最終一致性,如下文章即是介紹並整理該cache-aside-pattern方案的內容。數據庫
對於緩存中的數據,咱們提出三個目標:緩存
對於緩存與數據庫的雙寫問題,無外乎「增刪改查」 這四個過程,再考慮進併發的「讀讀,寫寫,讀寫」狀況,全部可能的狀況組合並很少。爲此,咱們能夠採用窮舉的方式對該方案進行說明。網絡
對於數據的查找,該方案的過程與cpu中查找數據的過程是一致的,其過程以下:併發
其示意圖以下:分佈式
在考慮高併發查詢的狀況下,對於該處理過程,以下示意圖所示。ide
當客戶端一、客戶端二、....、客戶端n查詢同一數據,且該數據不存在於緩存中時,全部的客戶端請求都會去訪問數據庫,此時會出現緩存擊穿的狀況,對於緩存擊穿的相關問題及解決方案,咱們留到下一篇文章再進行講解。高併發
對於新增數據的狀況,咱們將數據直接添加進數據庫中,且不將新增的數據加載入緩存中,其過程以下示意圖所示:性能
在考慮高併發之時,其過程以下示意圖所示:3d
經過示意圖,咱們瞭解到,當採用該種方式新增數據時,在併發狀況下,並未出現與咱們的目標相違背的問題。blog
當須要對數據進行刪除時,咱們有兩種刪除的方案可供選擇。
第一種: 先刪除緩存中的數據,再刪除數據庫中的數據
第二種: 先刪除數據庫中的數據,再刪除緩存中的數據
咱們對這兩種方案分別進行分析:
先刪緩存:
對於先刪除緩存中的數據,後刪除數據庫中的數據這種方案,其示意圖以下:
考慮進高併發的狀況,當存在讀請求和刪除數據的請求併發時,因爲網絡的不可靠和延時問題,其可能出現以下示意圖所示的狀況:
經過示意圖咱們瞭解到,存在着緩存中緩存進已刪除的舊數據的狀況,這違背了咱們提出的第一個目標。
那麼,先刪除數據庫中的數據的狀況呢?會出現這樣的問題嗎?咱們接着分析。
先刪數據庫:
對於先刪除數據庫中的數據,後刪除緩存中的數據這種方案,其示意圖以下。
一樣考慮進高併發的狀況,當存在讀請求和刪除數據的請求併發時,其發生的狀況以下示意圖所示:
此時,並未出現與咱們的目標相違背的問題。綜上,咱們在刪除數據時,應選擇先刪除數據庫中的數據再刪除緩存中的數據的這一方案。
可是該方案仍存在着問題。咱們再往下思考,當刪除了緩存中的相關數據,此時來了大量讀取該數據的請求。這時,就會致使「緩存穿透」問題的出現(該問題一樣在下一篇文章中進行講解)。
當須要對數據進行變動的時候,咱們有三種方案可供選擇。
第一種: 先更新數據庫,後更新緩存
第二種: 先刪除緩存,後更新數據庫
第三種: 先更新數據庫,後刪除緩存
咱們對這三種方案逐一進行分析。
先更新數據庫,後更新緩存:
對於先更新數據庫,後更新緩存這種方案,其示意圖以下:
考慮高併發的狀況,當存在兩個更新操做的併發請求時,因爲網絡的延時問題,其可能會出現以下這種狀況:
由上述示意圖可知,當存在兩個更新操做時,其有將數據庫中的舊數據存入緩存中的狀況,這違背了咱們的第一個目標。那麼,有沒有辦法解決呢?答案是有的,藉助樂觀鎖的相關思想,咱們能夠給每一個數據加上一個版本號,當數據要存入緩存時,比較一下數據的版本號便可。到目前爲止,更新了數據庫中的相關數據以後,再更新緩存中的數據這個方案看起來是能夠的,可是這個方案存在着一個問題,就是須要去更新緩存中的數據,更新緩存中的數據這個操做會影響系統的性能。特別是在寫多讀少,且緩存的數據不是直接從數據庫中存入,而是通過計算以後再進行緩存的業務場景中時,對系統性能形成的影響會更加明顯。爲此咱們能夠藉助「懶加載」的思想,在更新數據之時,將緩存中的數據進行刪除,等到須要用到的時候,纔將數據加載進緩存中。下面咱們將討論在更新時刪除緩存的兩個可能的方案。
先刪除緩存,後更新數據庫:
對於先刪除緩存,後更新數據庫的方案,其示意圖以下:
考慮高併發的狀況,當存在讀請求和更新請求併發的狀況,因爲存在網絡延時,其可能出現以下示意圖中的狀況:
由示意圖中的狀況可知,在讀寫併發的狀況下,其存在着將數據庫中的舊數據存入緩存中的問題。這違背了咱們的第一個目標。
先更新數據庫,後刪除緩存:
對於先更新數據庫,後刪除緩存的狀況,其以下示意圖所示:
考慮併發的狀況,當存在讀請求和更新請求併發,且此時緩存中的數據剛好失效時,再加上因爲網絡的延遲問題,則有可能會出現以下示意圖所示的狀況。
由示意圖可知,當緩存中的數據剛好失效時,其是可能存在着緩存中存入數據庫中的舊數據的可能性的。可是,發生這種狀況的機率是比較低的,由於讓該種狀況出現的條件較爲「苛刻」,須要剛好緩存中的數據失效,且要求「讀操做」慢於「寫操做」。但在一般狀況下,「寫操做」是慢於「讀操做」的,由於寫操做通常會涉及到數據庫鎖的相關操做。
綜上,在對比了以上三種方案以後,對於數據更改的狀況,咱們採用先更新數據庫,後刪除緩存的方式。