緩存設計——緩存和數據庫的數據一致性

緩存設計的誤區

咱們一般是這樣設計的,應用程序先從cache取數據,沒有獲得,則從數據庫中取數據,成功後,放到緩存中。數據庫

那試想一下,若是取出來的null,需不須要放入cache呢?答案固然是須要的。緩存

咱們試想一下若是取出爲null不放入cache會有什麼結果?很顯然每次取cache沒有走db返回null,很容易讓攻擊者利用這個漏洞搞垮你的服務器,利用洪水攻擊讓你的程序夯在這個地方致使你的正常流程搶不到資源。安全

 

數據庫和緩存雙寫一致性方案解析

在讀取緩存方面,你們沒啥疑問,都是按照下圖的流程來進行業務操做。服務器

可是在更新緩存方面,有如下幾種策略:網絡

  1. 先更新數據庫,再更新緩存
  2. 先刪除緩存,再更新數據庫
  3. 先更新數據庫,再刪除緩存

通常如今採用的是先更新數據庫,再刪除緩存。併發

這有篇寫的極好的博客可好好學習:分佈式之數據庫和緩存雙寫一致性方案解析分佈式

現將主要內容摘錄以下:ide

(1)先更新數據庫,再更新緩存

這套方案,你們是廣泛反對的。爲何呢?有以下兩點緣由。
緣由一(線程安全角度
同時有請求A和請求B進行更新操做,那麼會出現
(1)線程A更新了數據庫
(2)線程B更新了數據庫
(3)線程B更新了緩存
(4)線程A更新了緩存
這就出現請求A更新緩存應該比請求B更新緩存早纔對,可是由於網絡等緣由,B卻比A更早更新了緩存。可能在DB和memcached中具備相同數據項的不一樣值。這就致使了髒數據,所以不考慮。memcached

緣由二(業務場景角度)
有以下兩點:
(1)若是你是一個寫數據庫場景比較多,而讀數據場景比較少的業務需求,採用這種方案就會致使,數據壓根還沒讀到,緩存就被頻繁的更新,浪費性能。
(2)若是你寫入數據庫的值,並非直接寫入緩存的,而是要通過一系列複雜的計算再寫入緩存。那麼,每次寫入數據庫後,都再次計算寫入緩存的值,無疑是浪費性能的。顯然,刪除緩存更爲適合。性能

 

(2)先刪緩存,再更新數據庫

該方案會致使不一致的緣由是。同時有一個請求A進行更新操做,另外一個請求B進行查詢操做。那麼會出現以下情形:
(1)請求A進行寫操做,刪除緩存
(2)請求B查詢發現緩存不存在
(3)請求B去數據庫查詢獲得舊值
(4)請求B將舊值寫入緩存
(5)請求A將新值寫入數據庫

 

(3)先更新數據庫,再刪緩存

首先,先說一下。老外提出了一個緩存更新套路,名爲《Cache-Aside pattern》。其中就指出

失效:應用程序先從cache取數據,沒有獲得,則從數據庫中取數據,成功後,放到緩存中。

命中:應用程序從cache中取數據,取到後返回

更新:先把數據存到數據庫中,成功後,再讓緩存失效。

另外,知名社交網站facebook也在論文《Scaling Memcache at Facebook》中提出,他們用的也是先更新數據庫,再刪緩存的策略。
這種狀況不存在併發問題麼?
不是的。假設這會有兩個請求,一個請求A作查詢操做,一個請求B作更新操做,那麼會有以下情形產生
(1)緩存恰好失效
(2)請求A查詢數據庫,得一箇舊值
(3)請求B將新值寫入數據庫
(4)請求B刪除緩存
(5)請求A將查到的舊值寫入緩存
ok,若是發生上述狀況,確實是會發生髒數據。
然而,發生這種狀況的機率又有多少呢?
發生上述狀況有一個先天性條件,就是步驟(3)的寫數據庫操做比步驟(2)的讀數據庫操做耗時更短,纔有可能使得步驟(4)先於步驟(5)。但是,你們想一想,數據庫的讀操做的速度遠快於寫操做的(否則作讀寫分離幹嗎,作讀寫分離的意義就是由於讀操做比較快,耗資源少),所以步驟(3)耗時比步驟(2)更短,這一情形很難出現。

相關文章
相關標籤/搜索