聊聊db和緩存一致性常見的實現方式

2019-10-22     程式設計師聖經
來源公眾號路人甲Java 
作者路人甲Java

數據存儲在資料庫中,為了加快業務訪問的速度,我們將資料庫中的一些數據放在緩存中,那麼問題來了,如何確保db和緩存中數據的一致性呢?我們列出了5種方法,大家都了解一下,然後根據業務自己選擇。

方案1

獲取緩存邏輯

使用過定時器,定時刷新redis中的緩存。

db更新數據邏輯

更新數據不用考慮緩存中的數據,直接更新數據就可以了

存在的問題

緩存中數據和db中數據一致性可能沒有那麼及時,不過最終在某個時間點,數據是一致的。

方案2

獲取緩存邏輯

c1:根據key在redis中獲取對應的value

c2:如果value存在,直接返回value;若value不存在,繼續下面步驟

c3:從資料庫獲取值,賦值給value,然後將key->value放入redis,返回value

更新db邏輯

u1:開始db事務

u2:更新數據

u3:提交db事務

u4:刪除redis中當前數據的緩存

存在的問題

  1. 上面u3成功,u4失敗,會導致刪除緩存失敗,導致緩存中數據和db數據會不一致。
  2. 如果同時有很多線程到達c2發現緩存不存在,同時請求c3訪問db,會對db造成很大的壓力

方案3

獲取緩存邏輯

c1:根據key在redis中獲取對應的value

c2:如果value存在,直接返回value;若value不存在,繼續下面步驟

c3:從資料庫獲取值,賦值給value,然後將key->value放入redis,返回value

更新db邏輯

u1:刪除redis中當前數據的緩存

u2:開始db事務

u3:更新數據

u4:提交db事務

存在的問題

  1. 更新數據的線程執行u1成功之後,u2還未執行時,此時獲取緩存的線程剛好執行了c1到c3的邏輯,此時會將舊的數據放入redis,導致redis和db數據不一致
  2. 同樣存在方案2中說到的問題:如果同時有很多線程到達c2發現緩存不存在,同時請求c3訪問db,會對db造成很大的壓力

方案4

對方案2做改進,確保db更新成功之後,刪除緩存操作一定會執行,我們可以通過可靠消息來實現,可靠消息可以確保更新db操作和刪除redis中緩存最終要麼都成功要麼都失敗,依靠的是最終一致性來實現的。

改進之後過程如下。

獲取緩存邏輯

c1:根據key在redis中獲取對應的value

c2:如果value存在,直接返回value;若value不存在,繼續下面步驟

c3:從資料庫獲取值,賦值給value,然後將key->value放入redis,返回value

更新db邏輯

u1:開始db事務

u2:更新數據

u3:投遞刪除redis緩存的消息

u4:提交db事務

消息消費者-清理redis緩存的消費者

接受到清理redis緩存的消息之後,將redis中對應的緩存清除。

存在的問題

  1. 更新db和清理redis中的緩存之間存在一定的時間延遲,這段時間內,redis緩存的數據是舊的,也就是說這段時間內db和緩存數據是不一致的,但是最終會一致,這個不一致的時間可能比較小(這個需要看消息消費的效率了)
  2. 同樣存在方案2中說到的問題:如果同時有很多線程到達c2發現緩存不存在,同時請求c3訪問db,會對db造成很大的壓力

方式5

我們先了解一些知識。

redis中幾個方法

get(key)

獲取key的值,如果存在,則返回;如果不存在,則返回nil

setnx(key,value)

setnx的含義就是SET if Not Exists,該方法是原子的,如果key不存在,則設置當前key成功,返回1;如果當前key已經存在,則設置當前key失敗,返回0

del(key)

將key對應的值從redis中刪除

資料庫相關知識

select v from t where t.key = #key# for update;

update t set v = #v# where t.key = #key#;

上面兩個sql會相互阻塞,直到其中一個提交之後,另外一個才可以繼續執行。

下面我們就通過上面的知識來實現db和緩存強一致性。

更新數據邏輯

1.打開db事務
2.update t set v = #v# where t.key = #key#;
3.根據key刪除redis中的緩存:RedisUti.del(key);
4.提交db事務

獲取緩存邏輯

這種方式可以確保db和redis中緩存同一時間強一致,有問題的可以留言交流!

文章來源: https://twgreatdaily.com/zh-cn/Vt3r9m0BMH2_cNUgbn_L.html