一個應用中決定加緩存(Redis,memcached)以前,要考慮的第一個問題就是,引進了緩存以後,會帶來哪些收益(利),付出哪些代價,引發哪些額外的問題(弊)?
任何新的中間件引進,收益和成本都是伴隨的,只有當利大於弊的狀況下,可以容忍其弊端(完全解決?沒有額外代價又沒有負面影響,是不可能的,那就是不用就好了),才值得引進。
以Redis做爲緩存爲例,引進以後,其利和弊也是伴隨的。
帶來的收益:加速讀寫,提升併發性,下降後端持久化層數據庫的負載
付出的代價:增長代碼複雜,緩存自己的運維,潛在的數據不一致形成的影響。
數據不一致的存在
引進Redis(或者其餘緩存)以後,應用程序到持久化層多了一箇中間層,部分數據存儲由原來的單一持久化層,變爲緩存層和持久化層兩份。
這兩部分數據在相互同步的過程當中,在某些時間點上的維度來看,可能會潛在不一致的狀況。
其中,潛在的數據不一致,是任何一個引進緩存層以後最面臨的最大的一個問題(緩存層和持久化層,最終的數據是要保持一致的,這一點是底線)。
首先須要衡量的就是,這種潛在的不一致,會引起什麼樣的問題,帶來的問題是否能夠接受範圍以內,或者是否會對應用程序邏輯引發致命的問題。
緩存和持久化層存儲可能會不一致,每每是緩存和持久化層未同步刷新引發的,
具體舉例說明:
第一種狀況,好比點贊次數,瀏覽次數等等(讀多寫少的場景,寫MySQL,讀Redis,寫入了數據庫可是還沒有同步到緩存層這個間隙)。
不會對業務產生嚴重的邏輯錯誤,這種暫時性的數據不一致是能夠忍受的,另外就是,經過刷新等手段,二者數據最終會達成一致。
第二種狀況,好比銀行卡取款取超,致使餘額爲負數,緩存和持久化層存儲的不一致形成嚴重的邏輯錯誤,這種是沒法忍受的。
就須要考慮這種緩存層自己的設計是否合理?
輕量級作法,代碼邏輯實現
若是對於緩存的合理性沒有問題,且業務邏輯上要求緩存和持久化層強一致,那麼久要實現數據庫的一致性操做。
對於緩存和持久化層數據的一致性實現,我的的話,思路有如下兩種,
輕量級的作法以下:
對於引發數據變化的邏輯,通常都是「寫操做」,好比對數據的update或者delete或者insert操做,
1,首先去delete緩存中對應的數據(而不是去對應的update、delete、insert,爲何?由於只要delete成功,緩存被清理以後,就消除了不一致的可能性,而非delete就作不到)
2,若是1執行成功,再去操做持久化層的數據庫,
3,最後將寫入成功以後的持久化層數據回寫緩存層(這一步可選,或者其它手段同步)
重量級分佈式鎖實現,雙寫實現強一致
雙寫的安全性通常要經過分佈式鎖來實現,分佈式鎖能夠經過zookeeper或者redis實現。
一旦考慮使用分佈式鎖,又要考慮分佈式鎖的載體的安全性,也即不論是用zookeeper或者redis,要考慮zookeeper或者redis的安全性(集羣)。
這樣下去,問題會變得很是複雜,純粹變爲解決問題-->引入新的問題-->解決問題的死循環。
若是要保持一致,固然雙寫也是一種選擇,不過經過雙寫來確保數據的絕對一致,不但會對總體效率產生負面的影響,實現也是比較困難的,暫時不討論這種方案。redis
若是是分佈式鎖,任何寫入性操做,好比update,delete等,以下:
1,直接鎖定相關key值
2,依次操做緩存層和持久化層,同時作好每一層的回滾操做,一旦任何一步失敗,都要回滾
3,最終無論成功或者失敗,都釋放Key
分佈式鎖這種方式的話,實現起來,原代碼中業務侵入性較多,比較複雜