Redis緩存如何設計

高併發緩存 HighConcurreyCache

七大經典問題

1. 緩存集中失效(緩存擊穿?)

當業務系統查詢數據時,首先會查詢緩存,如果緩存中數據不存在,然後查詢DB再將數據預熱到Cache中,並返回。緩存的性能比DB 高50~100 倍以上。

緩存集中失效

很多業務場景,如:秒殺商品、微博熱搜排行、或者一些活動數據,都是通過跑任務方式,將DB數據批量、集中預熱到緩存中,緩存數據有著近乎相同的過期時間。

當過這批數據過期時,會一起過期,此時,對這批數據的所有請求,都會出現緩存失效,從而將壓力轉嫁到DB,DB的請求量激增,壓力變大,響應開始變慢。

解法
  1. 我們可以從緩存的過期時間入口,將原來的固定過期時間,調整為過期時間=基礎時間+隨機時間,讓緩存慢慢過期,避免瞬間全部過期,對DB產生過大壓力。
  2. 異步定時更新
    • 在緩存處理上,同理,比如某一個熱點數據的過期時間是1小時,那麼每59分鐘,通過定時任務去更新這個熱點key,並重新設置其過期時間。
  3. 互斥鎖
    • 在緩存處理上,通常使用一個互斥鎖來解決緩存擊穿的問題。簡單來說就是當Redis中根據key獲得的value值為空時,先鎖上,然後從數據庫加載,加載完畢,釋放鎖。若其他線程也在請求該key時,發現獲取鎖失敗,則先阻塞。. 只有一個線程去後端查詢
    • 但是會降低吞吐量

2. 緩存穿透

不是所有的請求都能查到數據,不論是從緩存中還是DB中。

假如黑客攻擊了一個論壇,用了一堆肉雞訪問一個不存的帖子id。按照常規思路,每次都會先查緩存,緩存中沒有,接著又查DB,同樣也沒有,此時不會預熱到Cache中,導致每次查詢,都會cache miss。

由於DB的吞吐性能較差,會嚴重影響系統的性能,甚至影響正常用戶的訪問。

緩存穿透 Cache Penetration

解法
  1. 為這些key對應的值設置為null並放到緩存中,
    • 這樣再出現查詢這個key的請求的時候,直接返回null即可, 但是還需要注意的就是需要有一個失效時間 . set key val ex 5nx
  2. BloomFilter
    • 緩存穿透是因為有很多惡意流量的請求,這些請求可能隨機生成很多Key來請求查詢,這些肯定在緩存和數據庫中都沒有,那就很容易導致緩存穿透
    • 比如如果有一群人經常來門店問一些根本不存在的色號,比如五彩斑斕的黑,這些色號該品牌根本沒生產過的話,店員就可以直接告訴顧客不存在就行了,也不需要驚動總部。
    • 布隆過濾器是一種比較巧妙的概率性數據結構,它可以告訴你數據一定不存在或可能存在,相比Map、Set、List等傳統數據結構它佔用內存少、結構更高效。

對於緩存穿透,我們可以將查詢的數據條件都哈希到一個足夠大的布隆過濾器中,用戶發送的請求會先被布隆過濾器攔截,一定不存在的數據就直接攔截返回了,從而避免下一步對數據庫的壓力。


3. 緩存雪崩

緩存雪崩 Cache Avalanche

緩存雪崩是指當大量緩存同時過期或緩存服務當機,所有請求的都直接訪問數據庫,造成數據庫高負載,影響性能,甚至數據庫當機。

緩存雪崩是指部分緩存節點不可用,進而導致整個緩存體系甚至服務系統不可用的情況。

分佈式緩存設計一般選擇一致性Hash,當有部分節點異常時,採用 rehash 策略,即把異常節點請求平均分散到其他緩存節點。但是,當較大的流量洪峰到來時,如果大流量key 比較集中,正好在某1~2 個緩存節點,很容易將這些緩存節點的內存、網卡過載,緩存節點異常Crash,然後這些異常節點下線,這些大流量key 請求又被rehash 到其他緩存節點,進而導致其他緩存節點也被過載Crash,緩存異常持續擴散,最終導致整個緩存體系異常,無法對外提供服務。

解法

  1. 不同的過期時間
    • 為了避免大量的緩存在同一時間過期,可以把不同的key過期時間設置成不同的, 並且通過定時刷新的方式更新過期時間。
  2. 集群 : 存增加多個副本,當緩存異常時,再讀取其他緩存副本
    • 為了避免門店出問題導致大量顧客直接打電話到總部,可以考慮開更多的門店,將用戶分流到多個店鋪中。
    • 在緩存雪崩問題防治上面,一個比較典型的技術就是採用集群方式部署,使用集群可以避免服務單點故障。
    • 為了保證副本的可用性,盡量將多個緩存副本部署在不同機架上,降低風險。
  3. 增加實時監控,及時預警。通過機器替換、各種故障自動轉移策略,快速恢復緩存對外的服務能力

4. 緩存熱點

對於突發事件,大量用戶同時去訪問熱點信息,這個突發熱點信息所在的緩存節點就很容易出現過載和卡頓現象,甚至Crash,我們稱之為緩存熱點。 這個在新浪微博經常遇到,某大V明星出軌、結婚、離婚,瞬間引發數百千萬的吃瓜群眾圍觀,訪問同一個key,流量集中打在一個緩存節點機器,很容易打爆網卡、帶寬、CPU的上限,最終導致緩存不可用。

解法

  • 首先能先找到這個熱key來,比如通過Spark實時流分析,及時發現新的熱點key。
  • 將集中化流量打散,避免一個緩存節點過載。由於只有一個key,我們可以在key的後面拼上有序編號,比如key#01、key#02。 。 。 key#10多個副本,這些加工後的key位於多個緩存節點上。
  • 每次請求時,客戶端隨機訪問一個即可 可以設計一個緩存服務治理管理後台,實時監控緩存的SLA,並打通分佈式配置中心,對於一些hot key可以快速、動態擴容。
  • singlefight

5. Big key

當訪問緩存時,如果key對應的value過大,讀寫、加載很容易超時,容易引發網絡擁堵。另外緩存的字段較多時,每個字段的變更都會引發緩存數據的變更,頻繁的讀寫,導致慢查詢。如果大key過期被緩存淘汰失效,預熱數據要花費較多的時間,也會導致慢查詢。

所以我們在設計緩存的時候,要注意緩存的粒度,既不能過大,如果過大很容易導致網絡擁堵;也不能過小,如果太小,查詢頻率會很高,每次請求都要查詢多次。

解法

  1. 設置一個閾值,當value的長度超過閾值時,對內容啟動壓縮,降低kv的大小
  2. 評估大key所佔的比例,由於很多框架採用池化技術,如:Memcache,可以預先分配大對象空間。真正業務請求時,直接拿來即用。
  3. 顆粒劃分,將大key拆分為多個小key,獨立維護,成本會降低不少
  4. 大key要設置合理的過期時間,盡量不淘汰那些大key

6. 緩存數據一致性

緩存是用來加速的,一般不會持久化儲存。所以,一份數據通常會存在DB和緩存中,由此會帶來一個問題,如何保證這兩者的數據一致性。另外,緩存熱點問題會引入多個副本備份,也可能會發生不一致現象。 緩存數據一致性

解法

  1. 當緩存更新失敗後,進行重試,如果重試失敗,將失敗的key寫入MQ消息隊列,通過異步任務補償緩存,保證數據的一致性。
  2. 設置一個較短的過期時間,通過自修復的方式,在緩存過期後,緩存重新加載最新的數據

7. 數據並發競爭預熱 (緩存擊穿?)

互聯網系統典型的特點就是流量大,一旦緩存中的數據過期、或因某些原因被刪除等,導致緩存中的數據為空,大量的並發線程請求(查詢同一個key)就會一起並發查詢數據庫,數據庫的壓力陡然增加。 數據並發競爭預熱

如果請求量非常大,全部壓在數據庫,可能把數據庫壓垮,進而導致整個系統的服務不可用。

解法

  1. 引入一把全局鎖(single fight),當緩存未命中時,先嘗試獲取全局鎖,如果拿到鎖,才有資格去查詢DB,並將數據預熱到緩存中。雖然,client端發起的請求非常多,但是由於拿不到鎖,只能處於等待狀態,當緩存中的數據預熱成功後,再從緩存中獲取

數據並發競爭預熱_method1

為了便於理解,簡單畫了個流程圖。這裡面特別注意一個點,由於有一個並發時間差,所以會有一個二次check緩存是否有值的校驗,防止緩存預熱重複覆蓋。
  1. 緩存數據創建多個備份,當一個過期失效後,可以訪問其他備份。
  2. 異步定時更新
    • 在緩存處理上,同理,比如某一個熱點數據的過期時間是1小時,那麼每59分鐘,通過定時任務去更新這個熱點key,並重新設置其過期時間。

Reference

© Kimi Tsai all right reserved.            Updated : 2023-07-12 09:04:53

results matching ""

    No results matching ""

    results matching ""

      No results matching ""