09丨缓存异常:如何解决缓存和数据库的数据不一致问题
1.緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致是如何發(fā)生的?
??首先,我們得清楚“數(shù)據(jù)的一致性”具體是啥意思。其實(shí),這里的“一致性”包含了兩種情況:
- 緩存中有數(shù)據(jù),那么,緩存的數(shù)據(jù)值需要和數(shù)據(jù)庫(kù)中的值相同;
- 緩存中本身沒有數(shù)據(jù),那么,數(shù)據(jù)庫(kù)中的值必須是最新值。
??對(duì)于讀寫緩存來說,如果要對(duì)數(shù)據(jù)進(jìn)行增刪改,就需要在緩存中進(jìn)行,同時(shí)還要根據(jù)采取的寫回策略,決定是否同步寫回到數(shù)據(jù)庫(kù)中。
同步直寫策略:寫緩存時(shí),也同步寫數(shù)據(jù)庫(kù),緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致;
- 異步寫回策略:寫緩存時(shí)不同步寫數(shù)據(jù)庫(kù),等到數(shù)據(jù)從緩存中淘汰時(shí),再寫回?cái)?shù)據(jù)庫(kù)。使用這種策略時(shí),
- 如果數(shù)據(jù)還沒有寫回?cái)?shù)據(jù)庫(kù),緩存就發(fā)生了故障,那么,此時(shí),數(shù)據(jù)庫(kù)就沒有最新的數(shù)據(jù)了。
2. 如何解決數(shù)據(jù)不一致問題?
??重試機(jī)制具體來說,可以把要?jiǎng)h除的緩存值或者是要更新的數(shù)據(jù)庫(kù)值暫存到消息隊(duì)列中(例如使用Kafka 消息隊(duì)列)。當(dāng)應(yīng)用沒有能夠成功地刪除緩存值或者是更新數(shù)據(jù)庫(kù)值時(shí),可以從消息隊(duì)列中重新讀取這些值,然后再次進(jìn)行刪除或更新。
??當(dāng)有大量并發(fā)請(qǐng)求時(shí),應(yīng)用還是有可能讀到不一致,按照不同的刪除和更新順序,分成兩種情況來看。在這兩種情況下,我們的解決方法也有所不同。
情況一:先刪除緩存,再更新數(shù)據(jù)庫(kù)。
??假設(shè)線程 A 刪除緩存值后,還沒有來得及更新數(shù)據(jù)庫(kù)(比如說有網(wǎng)絡(luò)延遲),線程 B 就開始讀取數(shù)據(jù)了,那么這個(gè)時(shí)候,線程 B 會(huì)發(fā)現(xiàn)緩存缺失,就只能去數(shù)據(jù)庫(kù)讀取。這會(huì)帶來兩個(gè)問題:
- 線程 B 讀取到了舊值;
- 線程 B 是在緩存缺失的情況下讀取的數(shù)據(jù)庫(kù),所以,它還會(huì)把舊值寫入緩存,這可能會(huì)導(dǎo)致其他線程從緩存中讀到舊值。
在線程 A 更新完數(shù)據(jù)庫(kù)值以后,我們可以讓它先 sleep 一小段時(shí)間,再進(jìn)行一次緩存刪除操作。
情況二:先更新數(shù)據(jù)庫(kù)值,再刪除緩存值。
??如果線程 A 刪除了數(shù)據(jù)庫(kù)中的值,但還沒來得及刪除緩存值,線程 B 就開始讀取數(shù)據(jù)了,那么此時(shí),線程 B 查詢緩存時(shí),發(fā)現(xiàn)緩存命中,就會(huì)直接從緩存中讀取舊值。不過,在這種情況下,如果其他線程并發(fā)讀緩存的請(qǐng)求不多,那么,就不會(huì)有很多請(qǐng)求讀取到舊值。而且,線程 A 一般也會(huì)很快刪除緩存值,這樣一來,其他線程再次讀取時(shí),就會(huì)發(fā)生緩存缺失,進(jìn)而從數(shù)據(jù)庫(kù)中讀取最新值。所以,這種情況對(duì)業(yè)務(wù)的影響較小。
??對(duì)于讀寫緩存來說,如果我們采用同步寫回策略,那么可以保證緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致。只讀緩存的情況比較復(fù)雜,我總結(jié)了一張表,以便于你更加清晰地了解數(shù)據(jù)不一致的問題原因、現(xiàn)象和應(yīng)對(duì)方案。
3. 如何解決緩存雪崩、擊穿、穿透
總結(jié)
以上是生活随笔為你收集整理的09丨缓存异常:如何解决缓存和数据库的数据不一致问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 08 | 替换策略: 缓存满了怎么办?
- 下一篇: 10丨 Redis主从同步与故障切换,有