談談一致性
一致性是指數據保持一致,在分布式系統中,可以理解為多個節點中的數據是一致的。
- 強一致性:用戶寫入什么數據,就可以讀出什么數據。這種一致性最符合用戶的直覺,用戶體驗好,但實現起來往往對系統的性能影響最大。
- 弱一致性:在用戶寫入系統成功后,不承諾可以立即讀出寫入的數據,也不承諾多久數據可以達到一致,但會盡可能地保證到某個時間級別(比如秒級別)后,數據能夠達到一致狀態。
- 最終一致性:最終一致性是弱一致性的一種特例,系統會保證在一定時間內,數據能夠達到一致的狀態。這里之所以將最終一致性單獨提出來,是因為它是弱一致性中非常推崇的一種一致性模型,也是業界在大型分布式系統的數據一致性上比較推崇的模型。
集中式redis緩存的三個經典的緩存模式
緩存可以提升性能,緩解數據庫壓力,但是緩存也會造成數據不一致的問題。那么一般是如何使用緩存的呢?
- 旁路緩存模式,Cache-Aside Pattern
- 讀寫穿透,Read-Through/Write-Through Pattern
- 寫流程,Write behind
Cache-Aside Pattern
旁路緩存模式的讀流程:先判斷是否命中緩存,未命中則查詢數據庫並更新緩存。
旁路緩存模式寫流程:更新數據庫,然后刪除緩存。
Read-Through/Write-Through 讀寫穿透
在讀寫穿透模式中,服務端把緩存作為主要數據存儲。應用程序跟數據庫緩存交互,都是通過抽象緩存層完成的。
Read-Through讀流程:
從緩存中讀數據,讀到直接返回
未讀到則查詢數據庫,然后更新緩存,再返回結果。
這個流程和旁路緩存模式很像,只是在查詢流程中增加了一層Cache-Provider,流程如下:
我認為這層封裝目的是,讓使用者可以避免將未命中的查庫更新緩存的場景,反復編寫;使使用者更簡單的時候緩存模式。
Write-Through寫流程:
當發生寫請求時,也是由緩存抽象層完成數據源和緩存數據的更新,流程如下:
Write behind(異步緩存寫入)
Write behind跟Read-Through/Write-Through有相似的地方,都是由Cache Provider來負責緩存和數據庫讀寫。它兩個又有很大的不同,讀寫穿透是同步更新緩存和數據庫,異步寫入緩存則是只更新緩存,不直接更新數據庫,通過批量異步的方式來更新數據庫。
這種方式下,緩存和數據庫的一致性不強,對一致性要求高的系統要謹慎使用。
但是它適合頻繁寫的場景,MySQL的InnoDB Buffer Pool機制就使用到這種模式。
三種模式的比較
1、旁路緩存模式實現起來比較簡單,但是需要維護兩個數據存儲:
- 一個是緩存
- 一個是數據庫
2、讀寫穿透模式的寫模式需要維護一個數據存儲(緩存),實現起來要復雜一些。
3、異步寫模式與讀寫穿透模式相似,只是異步寫是異步的,讀寫穿透是同步的。
4、異步寫的優點是直接操作內存速度快,多次操作可以合並為一次持久化到數據庫。缺點是數據可能會丟失,例如系統斷電。
Cache-Aside的問題
更新數據的時候,Cache-Aside是刪除緩存呢?還是應該更新緩存呢?
有些小伙伴會問,為什么Cache-Aside寫入的時候,是刪除緩存而不是更新緩存呢?下面來看個例子:
操作的次序如下:
- 線程A先發起一個寫請求,先更新了數據庫
- 線程B又發起一個寫請求,又更新了數據庫
- 由於網絡原因,線程B先更新了緩存,然后線程A又更新了緩存。
- 這個時候,緩存中保存A的數據就是舊數據(數據庫中B更新的數據是新數據),數據庫不一致了,出現臟數據了。如果是刪除緩存就不會出現這個問題。
更新緩存還有兩個劣勢:
- 如果寫入的緩存值,是經過復雜計算才得到的話。更新頻率高的話,會浪費性能。
- 在寫多讀少的情況下,很多數據沒有被讀到,數據就又被更新了,也會浪費性能。