1. 程式人生 > 資料庫 >快取和資料庫資料的一致性

快取和資料庫資料的一致性

如何保證快取和資料庫資料的一致性


如何保證快取與資料庫的雙寫一致性?

概念:

使用快取的時候,一般情況下要求快取與資料庫中的資料保持一致
在寫入資料,對資料進行修改的時候,需要對快取中的資料和資料庫中的資料進行一起修改

使用快取策略(Cache Aside Pattern)

讀取資料:
首先嚐試從快取讀取,讀到資料則直接返回;如果讀不到,就讀資料庫,並將資料會寫到快取,並返回。
寫入資料:
需要更新資料時,先更新資料庫,然後把快取裡對應的資料失效掉(刪掉)。

為什麼是刪除快取,而不是更新快取?

快取不只是從資料庫中取出來的值,有時候通過計算產生的值也會放在快取中
還有更新快取的代價有時候是很高的。

有時候一個數據,可能更新的比較頻繁,但是不會經常訪問,比如更新200多次才訪問一次,如果同步進行更新快取,開銷就比較大
所以執行寫操作的時候,是刪除快取,等到要訪問這條資料,先去資料庫讀取, 然後寫入快取

資料不一致型別

  1. 資料庫有資料,快取沒有資料;
  2. 資料庫有資料,快取也有資料,資料不相等;
    在執行寫操作的時候,資料庫修改成功了,但是快取沒有刪除成功
  3. 資料庫沒有資料,快取有資料。
    也是刪除快取沒有成功

解決方案

  1. 對刪除快取進行重試,資料的一致性要求越高,我越是重試得快。

  2. 定期全量更新,簡單地說,就是我定期把快取全部清掉,然後再全量載入。

  3. 給所有的快取一個失效期
    任何不一致,都可以靠失效期解決,失效期越短,資料一致性越高。但是失效期越短,查資料庫就會越頻繁。因此失效期應該根據業務來定。

  4. 讀請求和寫請求序列化,串到一個記憶體佇列裡去。
    序列化可以保證一定不會出現不一致的情況,但是它也會導致系統的吞吐量大幅度降低,用比正常情況下多幾倍的機器去支撐線上的一個請求。

在併發不高的情況:

讀: 讀redis->沒有,讀mysql->把mysql資料寫回redis,有的話直接從redis中取;

寫: 寫mysql->成功,再寫redis;

可能會導致:
如果刪除快取失敗了,那麼會導致資料庫中是新資料,快取中是舊資料,資料就出現了不一致。

解決方案:
先刪除快取,再修改資料庫。如果資料庫修改失敗了,那麼資料庫中是舊資料,快取中是空的,那麼資料不會不一致。因為讀的時候快取沒有,則讀資料庫中舊資料,然後更新到快取中。

併發高的情況:

讀: 讀redis->沒有,讀mysql->把mysql資料寫回redis,有的話直接從redis中取;

:非同步話,先寫入redis的快取,就直接返回;定期或特定動作將資料儲存到mysql,可以做到多次更新,一次儲存;

在分散式環境下,資料的讀寫都是併發的,上游有多個應用,通過一個服務的多個部署(為了保證可用性,一定是部署多份的),對同一個資料進行讀寫,在資料庫層面併發的讀寫並不能保證完成順序,也就是說後發出的讀請求很可能先完成(讀出髒資料):

假設這會有兩個請求,一個請求A做查詢操作,一個請求B做更新操作,那麼會有如下情形產生

(1)快取剛好失效
(2)請求A查詢資料庫,得一箇舊值
(3)請求B將新值寫入資料庫
(4)請求B刪除快取
(5)請求A將查到的舊值寫入快取

但是出現這中情況的機率比較小,資料庫的讀操作的速度遠快於寫操作的,
因此(3)耗時比步驟(2)更短,這一情形很難出現。大部分情況都是,(2)比(3)快,所以(5)比(4)先出現

解決方案

(1)設定快取有效時間,快取定時失效
(2)使用非同步延時刪除策略,保證讀請求完成以後,再進行刪除操作。