Mysql和Redis數據同步策略

爲何對緩存只刪除不更新

不更新緩存是防止併發更新致使的數據不一致。
因此爲了下降數據不一致的機率,不該該更新緩存,而是直接將其刪除,
而後等待下次發生cache miss時再把數據庫中的數據同步到緩存。html

先更新數據庫仍是先刪除緩存?

有兩個選擇:
1. 先刪除緩存,再更新數據庫
2. 先更新數據庫,再刪除緩存mysql

若是先刪除緩存,有一個明顯的邏輯錯誤:考慮兩個併發操做,線程A刪除緩存後,線程B讀該數據時會發生Cache Miss,而後從數據庫中讀出該數據並同步到緩存中,此時線程A更新了數據庫。
結果致使,緩存中是老數據,數據庫中是新數據,而且以後的讀操做都會直接讀取緩存中的髒數據。(直到key過時被刪除或者被LRU策略踢出)
若是數據庫更新成功後,再刪除緩存,就不會有上面這個問題。
多是因爲數據庫優先,第二種方式也被稱爲Cache Aside Pattern。redis

Cache Aside Pattern

cache aside在絕大多數狀況下能作到數據一致性,可是在極端狀況仍然存在問題。sql

  • 首先更新數據庫(A)和刪除緩存(B)不是原子操做,任何在A以後B以前的讀操做,都會讀到redis中的舊數據。
    可是,正常狀況下操做緩存的速度會很快,一般是毫秒級,出現上述狀況的機率很低。
  • 更新完數據庫後,線程意外被kill掉,因爲沒有刪除緩存,緩存中的髒數據會一直存在。
  • 線程A讀數據時cache miss,從Mysql中查詢到數據,還沒來得及同步到redis中,
    此時線程B更新了數據庫並把Redis中的舊值刪除。隨後,線程A把以前查到的數據同步到了Redis。
    顯然,此時redis中的是髒數據。
    一般數據庫讀操做比寫操做快不少,因此除非線程A在同步redis前意外卡住了,不然發生上述狀況的機率極低。

雖然以上狀況都有可能發生,可是發生的機率相比「先刪除緩存再更新數據庫」會低不少。shell

Read/Write Through Pattern

cache aside是咱們本身的應用程序維護兩個數據存儲系統,而Read/Write Through Pattern是把同步數據的問題交給緩存系統了,應用程序不須要關心。
Read Through是指發生cache miss時,緩存系統自動去數據庫加載數據。
Write Through是指若是cache miss,直接更新數據庫,而後返回,若是cache hit,則更新緩存後,由緩存系統自動同步到數據庫。
以Redis爲例,一般咱們不會把數據庫的數據所有緩存到redis,而是採用必定的數據精簡或壓縮策略,以節省緩存空間。
就是說,讓緩存系統設計出通用的緩存方案不太現實,不過根據本身的業務定製一個在項目內部通用的中間件是可行的。數據庫

Write Behind

Write Behind方案在更新數據時,只更新緩存,不更新數據庫。而是由另一個服務異步的把數據更新到數據庫。
邏輯上,和Linux中的write back很相似。這個設計的好處是,I/O操做很快,由於是純內存操做。
可是因爲異步寫庫,可能要犧牲一些數據一致性,譬如忽然宕機會丟失全部未寫入數據庫的內存數據。緩存

阿里巴巴的Canal中間件是一種相反的設計,它先更新mysql,而後經過binlog把數據自動同步到redis。
這種方案會全量同步數據到redis,不適合只緩存熱點數據的應用。併發

總結

以上沒有哪一種方案是完美的,都沒法作到強一致性。
咱們總要在性能和數據準確性之間作出妥協。異步

https://www.pixelstech.net/article/1562504974-Consistency-between-Redis-Cache-and-SQL-Database
https://coolshell.cn/articles/17416.html
爲何不更新緩存,而是直接刪除ide

相關文章
相關標籤/搜索