如何保證MySQL和Redis資料一致性?

LvLaoTou發表於2024-04-01

背景


在高併發的業務場景中,因為MySQL資料庫是操作磁碟效率比較低,因此大多數情況下資料庫都是高併發系統的瓶頸。因為Redis運算元據是在記憶體中進行,所以就需要使用Redis做一個快取。讓請求先訪問到Redis,而不是直接訪問MySQL資料庫。效果圖如下

Untitled

查詢資料


上面的業務場景,就是一個典型的MySQL儲存資料和Redis快取資料的業務場景。下面來看看一般的查詢流程,如下圖:

Untitled

上面的查詢流程如下:

  1. 使用者請求系統,系統先查詢Redis中是否有資料?
  2. 如果Redis中有資料,則直接將快取中的資料響應給使用者。
  3. 如果Redis中沒有資料,則取查詢MySQL資料庫中是否有資料?
  4. 如果MySQL中有資料,則先將資料更新到Redis中,再將資料響應給使用者。
  5. 如果MySQL中沒有資料,則響應空資料給使用者,請求結束。

上面的查詢流程很簡單,透過先查Redis快取,避免大量請求訪問MySQL資料庫,從而大大提高系統響應效率。而且如果Redis中沒有而MySQL中有,當從MySQL拿到資料以後,先將資料更新到Redis快取中,這樣下次請求同一份資料的時候就能從Redis中獲取了。

更新資料


上面的查詢流程是沒什麼問題,可是資料很有可能會更新。那麼更新的時候怎麼操作才能保證Redis和MySQL中的資料都更新成功並且一致呢?

下面看看一種常用的解決方案(雙刪快取):

Untitled

第一個問題:為什麼是刪除Redis快取資料而不是更新資料?

假設我們快取的資料是要做一個很複雜的計算,而且還不一定能用到。那如果你更新MySQL資料之後去更新Redis快取不是就很耗時了,而且有可能做無用功。

第二個問題:為什麼是先刪除Redis快取資料而不是先更新資料庫呢?

我們不妨假設先更新MySQL中的資料,然後再刪除快取。如果更新完MySQL但是刪除Redis失敗了(別問為什麼會失敗?系統故障行不行,全球斷電行不行?),那下次查詢請求過來,因為Redis中有快取資料,所以直接返回Redis快取的舊資料了,是不是就出問題了?

那我們再看看,如果先刪除Redis快取,再更新MySQL。如果刪除完Redis成功,但是更新MySQL失敗。下次查詢的時候,查詢到Redis快取發現沒有,再去查MySQL,然後更新到Redis,雖然MySQL更新失敗了,但是Redis中的資料和MySQL是一致的。

第三個問題:為什麼更新完MySQL後還要再刪一次Redis快取呢?

假設我們第一次刪完Redis結束,正在更新MySQL但是還沒更新成功的時候,這時候有另外一個請求來查詢資料。第二個請求查詢Redis沒有,然後查詢MySQL這時候因為MySQL還沒更新完,所以查詢到的還是舊資料,同時把舊資料更新到Redis中了,等下一個請求再來查詢的時候發現Redis有資料,就直接返回舊資料了。

因此更新完MySQL後需要再次刪除Redis快取。這樣即使更新資料中間有其他個請求把舊資料更新到Redis中了,因為再次刪了Redis快取中的舊資料,依然能夠避免其他請求獲取到舊資料。

我們認為資料是否更新成功是以MySQL中的資料為準,因此MySQL還沒更新完成前或者更新失敗,獲取到舊資料不算是問題。所以我們只要保證Redis和MySQL中的資料一致就行。

相關文章