Redis
緩存穿透
-
定義:redis查詢一個不存在的數據,導致每次都查詢數據庫
-
解決方案:
-
如果查詢的數據為空,在redis對應的key緩存空數據,并設置短TTL。
因為緩存穿透通常是因為被惡意用不存在的查詢參數進行壓測攻擊,所以采取這種方式會導致大量的無用空間被占用,所以不推薦。
-
布隆過濾器,因為哈希沖突,所以可能誤判(計算出的下標位置會出現重復,導致不存在的元素被錯誤地判定為存在),實現框架:
Redisson
、Guava
。-
bitmap
(位圖):一個以bit為單位的數組,只存儲二進制01 -
原理:對于一個給定的 key,通過多個不同的 hash 函數 計算得到多個 hash 值,每個 hash 值對 m(bitmap 的大小) 進行 取模運算,得到對應的
bitmap
下標位置,并將這些位置的 bit 值設置為 1。讀取時,只需要判斷其對應的多個下標位置的元素值是否都為1即可 -
如何控制誤判率?調整參數:
bitmap
數組長度m、哈希函數個數k、元素數量n -
支持刪除操作嗎?不支持,除非使用計數型布隆過濾器(整數數組,存計數值,即添加操作+1,刪除操作-1,如果所有計數值都大于0時,元素才可能存在)
-
布隆過濾器相比使用
set
的優點?-
空間占用: 布隆過濾器相比
set
可以顯著節省空間,因為它不存儲完整的元素,而是通過哈希映射到bit位查詢速度: 布隆過濾器通過少量哈希計算(O(k))就能快速判斷元素是否存在,在元素數量非常多時,可能比set數據更快,因為set可能需要遍歷鏈表/紅黑樹
-
bitmap
都是01數據,適合集合運算(交、并、補)
-
-
-
緩存擊穿
-
定義:給一個key設置過期時間,當key過期時,恰好對這個key有大量并發請求,在重建緩存這段時間可能導致數據庫壓力過大
-
如何檢測?監控 Redis 的
keyspace_hits(命中緩存次數)
和keyspace_misses(未命中緩存次數)
,計算命中率 -
解決方案:
- 互斥鎖(保證強一致性):在Redis查詢緩存未命中時,添加互斥鎖,然后查詢數據庫并重建緩存,再釋放鎖
- 邏輯過期+互斥鎖(高效):對熱點key不設置過期時間,而是給對應的數據添加一個過期時間的字段。過程:
- 在查詢緩存時發現邏輯時間已經過期,此時添加互斥鎖
- 開啟新子線程:查詢數據庫并重建緩存,釋放互斥鎖
- 主線程和其他發起請求的線程不需要等待該子線程(trylock),而是直接返回過期數據
緩存雪崩
- 定義:在同一時段大量的緩存key同時失效或者Redis服務宕機,導致數據庫壓力過大
- 解決方案:
- key失效:給不同key的TTL的原值加上隨機值
- 服務宕機:Redis集群
- 通用:降級限流(nginx或spring cloud gateway),多級緩存
雙寫一致性
-
定義:數據庫中的數據需要與Redis中的數據保持一致
-
問題:
- 先刪除緩存再操作數據庫:在刪除完緩存到操作數據庫這段期間,另一個線程重建完緩存。
- 先操作數據庫再刪除緩存:在查詢完數據庫到重建緩存這段期間,另一個線程修改了數據庫并刪除緩存。
-
高一致性實現方案:
-
延遲雙刪:刪除緩存,修改數據庫,延遲一會再刪除緩存,延遲的原因是等待數據庫master同步到slave 。該方法的問題是延遲時間很難確定。
-
讀數據時添加共享鎖:其他線程可共享讀操作
寫數據時添加排他鎖:阻塞其他線程讀寫操作
-
-
弱一致性實現方案:
- MQ:修改數據庫時,發布消息到mq中,由消費者來刪除緩存,只能保證redis操作的可靠性
- Canal:監聽mysql的binlog(日志文件),把更新后的日志同步到redis里面