緩存使用需要考慮的一些細節


一、數據庫與緩存一致性

使用緩存,可以降低耗時,提供系統吞吐性能。但是,使用緩存,會存在數據一致性的問題。

1、幾種緩存使用模式

  • Cache-Aside Pattern,旁路緩存模式
  • Read-Through/Write-Through(讀寫穿透)
  • Write- behind (異步緩存寫入)

一般我們使用緩存,都是旁路緩存模式,讀請求流程如下:

  • 讀的時候,先讀緩存,緩存命中的話,直接返回數據;
  • 緩存沒有命中的話,就去讀數據庫,從數據庫取出數據,放入緩存后,同時返回響應。

旁路緩存模式的寫流程:

2、刪除緩存呢,還是更新緩存?

我們在操作緩存的時候,到底應該刪除緩存還是更新緩存呢?我們先來看個例子:

  • 線程A先發起一個寫操作,第一步先更新數據庫;
  • 線程B再發起一個寫操作,第二步更新了數據庫;
  • 由於網絡等原因,線程B先更新了緩存;
  • 線程A更新緩存。

這時候,緩存保存的是A的數據(老數據),數據庫保存的是B的數據(新數據),數據不一致了,臟數據出現啦。如果是刪除緩存取代更新緩存則不會出現這個臟數據問題。

3、先操作數據庫還是先操作緩存

雙寫的情況下,先操作數據庫還是先操作緩存?我們再來看一個例子:假設有A、B兩個請求,請求A做更新操作,請求B做查詢讀取操作。

  • 線程A發起一個寫操作,第一步del cache;
  • 此時線程B發起一個讀操作,cache miss;
  • 線程B繼續讀DB,讀出來一個老數據;
  • 然后線程B把老數據設置入cache;
  • 線程A寫入DB最新的數據;

醬紫就有問題啦,緩存和數據庫的數據不一致了。緩存保存的是老數據,數據庫保存的是新數據。因此,Cache-Aside緩存模式,選擇了先操作數據庫而不是先操作緩存。

4、如何保證最終一致性

  • 緩存延時雙刪
  • 刪除緩存重試機制
  • 讀取biglog異步刪除緩存

二、緩存穿透

1、原理

緩存穿透`:指查詢一個一定不存在的數據,由於緩存不命中時,需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,進而給數據庫帶來壓力。”

緩存穿透一般都是這幾種情況產生的:業務不合理的設計、業務/運維/開發失誤的操作、黑客非法請求攻擊。如何避免緩存穿透呢?

2、解決辦法

一般有三種方法。

  • 如果是非法請求,我們在API入口,對參數進行校驗,過濾非法值。

  • 如果查詢數據庫為空,我們可以給緩存設置個空值,或者默認值。但是如有有寫請求進來的話,需要更新緩存哈,以保證緩存一致性,同時,最后給緩存設置適當的過期時間。(業務上比較常用,簡單有效)

  • 使用布隆過濾器快速判斷數據是否存在。即一個查詢請求過來時,先通過布隆過濾器判斷值是否存在,存在才繼續往下查。


三、緩存雪崩

1、原理

緩存雪崩:指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。”

2、解決辦法

緩存雪奔一般是由於大量數據同時過期造成的,對於這個原因,可通過均勻設置過期時間解決,即讓過期時間相對離散一點。如采用一個較大固定值+一個較小的隨機值,5小時+0到1800秒醬紫。

Redis 故障宕機也可能引起緩存雪奔。這就需要構造Redis高可用集群啦。


四、緩存機擊穿

1、原理

緩存擊穿:指熱點key在某個時間點過期的時候,而恰好在這個時間點對這個Key有大量的並發請求過來,從而大量的請求打到db。”

緩存擊穿看着有點像緩存雪崩,其實它兩區別是,緩存雪奔是指數據庫壓力過大甚至down機,緩存擊穿只是大量並發請求到了DB數據庫層面。可以認為擊穿是緩存雪奔的一個子集吧。有些文章認為它倆區別,是在於擊穿針對某一熱點key緩存,雪奔則是很多key。

2、解決方法

解決方案就有兩種:

  • 使用互斥鎖方案。緩存失效時,不是立即去加載db數據,而是先使用某些帶成功返回的原子操作命令,如(Redis的setnx)去操作,成功的時候,再去加載db數據庫數據和設置緩存。否則就去重試獲取緩存。
  • “永不過期”。是指沒有設置過期時間,但是熱點數據快要過期時,異步線程去更新和設置過期時間。

五、緩存熱Key

1、原理

在Redis中,我們把訪問頻率高的key,稱為熱點key。如果某一熱點key的請求到服務器主機時,由於請求量特別大,可能會導致主機資源不足,甚至宕機,從而影響正常的服務。

2、解決方法

如何解決熱key問題?

  • Redis集群擴容:增加分片副本,均衡讀流量;
  • 對熱key進行hash散列,比如將一個key備份為key1,key2……keyN,同樣的數據N個備份,N個備份分布到不同分片,訪問時可隨機訪問N個備份中的一個,進一步分擔讀流量;
  • 使用二級緩存,即JVM本地緩存,減少Redis的讀請求。

六、緩存容量內存考慮

1、評估容量,合理利用

如果我們使用的是Redis,而Redis的內存是比較昂貴的,我們不要什么數據都往Redis里面塞,一般Redis只緩存查詢比較頻繁的數據。同時,我們要合理評估Redis的容量,也避免頻繁set覆蓋,導致設置了過期時間的key失效。

如果我們使用的是本地緩存,如guava的本地緩存,也要評估下容量。避免容量不夠。

2、Redis的八種內存淘汰機制

為了避免Redis內存不夠用,Redis用8種內存淘汰策略保護自己~

  • volatile-lru:當內存不足以容納新寫入數據時,從設置了過期時間的key中使用LRU(最近最少使用)算法進行淘汰;
  • allkeys-lru:當內存不足以容納新寫入數據時,從所有key中使用LRU(最近最少使用)算法進行淘汰。
  • volatile-lfu:4.0版本新增,當內存不足以容納新寫入數據時,在過期的key中,使用LFU算法進行刪除key。
  • allkeys-lfu:4.0版本新增,當內存不足以容納新寫入數據時,從所有key中使用LFU算法進行淘汰;
  • volatile-random:當內存不足以容納新寫入數據時,從設置了過期時間的key中,隨機淘汰數據。
  • allkeys-random:當內存不足以容納新寫入數據時,從所有key中隨機淘汰數據。
  • volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的key中,根據過期時間進行淘汰,越早過期的優先被淘汰;
  • noeviction:默認策略,當內存不足以容納新寫入數據時,新寫入操作會報錯。

3、不同的業務場景,Redis選擇適合的數據結構**

  • 排行榜適合用zset
  • 緩存用戶信息一般用hash
  • 消息隊列,文章列表適用用list
  • 用戶標簽、社交需求一般用set
  • 計數器、分布式鎖等一般用String類型


轉載

化解日常Bug的50個大法(覆蓋數據庫、代碼層面、緩存)



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM