redis快取擊穿,穿透,雪崩
redis快取雪崩
快取雪崩是指,有大量的快取在同一時刻過期或者redis宕機,造成大量的請求打入資料庫
解決方案:
redis設定過期時間儘量分散,設定隨機過期時間。
事前:保證高可用,叢集搭建,主從+哨兵,讀寫分離,cluster叢集
事中:本地ehcache快取 + hystrix 或 sentinel 熔斷&限流&降級,避免mysql被打死
事後:redis持久化,快速恢復快取資料,AOF,RDB
redis快取擊穿
快取擊穿是指一個熱點key失效,導致大量的請求打入資料庫。
解決方案:
設定熱點key永不過期
使用本地ehcache快取+hystrix或sentinel熔斷&限流&降級,避免mysql被打死
redis快取穿透
快取穿透是指惡意請求訪問,比如查詢id傳入負數,資料庫和快取都沒有這個資料
問題1:
假設黑客模擬大量請求訪問id為-1的資料,這個資料快取中肯定是沒有的,所以這些請求就打到資料庫上面了
解決方案1:
把空結果寫到快取中去,比如:key:-1 value:null expire:15s
問題2:
如果是-1,-2,-3這樣的請求,上面方法反而會起到適得其反的作用,導致redis記憶體大量消耗
解決方案2:
使用布隆過濾器,布隆過濾器可以返回一個key存在,則這個key可能存在,返回不存在,則一定不存在。
布隆過濾器原理:
布隆過濾器大概是一個數組,或者一個bitmap,一個key請求過來之後,會經過多個hash演算法運算,每個hash演算法不一樣,得到多個hash值,這個時候去記錄陣列對應下標為1;
列如:新增資料的時候,新增id為1的資料,經過多個hash運算,分別得到 1,3,5的結果
則把陣列的結果修改為int[] arr = {0,1,0,1,0,1,0,0,0,0,0,0,0}
id為2的資料來了,hash運算得到3,4,7
則把陣列的結果修改為{0,1,0,1,1,1,0,1,0,0,0,0,0}
查詢的時候,如果請求為1,那對應hash結果為1,3,5,去陣列找到對應下標的結果是否都為1,如果為1,說明這個id存在,可以允許訪問。
如果查詢-1,那hash運算結果為3,4,6,找對應下標,發現6不為1,所以這個id必不存在,這個請求無效。
問題很明顯:
如果布隆過濾器的長度不夠的話,可能會出現{1,1,1,1,1,1,1,1,1,1,1,1,1}的情況,那所有請求都會認為存在了。
無法刪除資料,對一個數據刪除的時候,無法去修改1為0,因為對應的下標可能是其他元素的hash運算結果。
hash演算法的個數需要巧妙設計,以及用哪些演算法。
不過不用擔心用googlet提供的,案例就不多寫了
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
還可以使用布穀鳥過濾器
布穀鳥過濾器原理:
鳩佔鵲巢的思想,一個一個擠走。
布穀鳥過濾器是在布隆過濾器基礎上的一個改進,他一樣計算出多個hash值,這個時候可以把多個hash值看作一個個巢,比如現在id為1的元素,他有3個巢為1,3,5.那他第一次會去1的巢穴,id為2的元素,如果計算得到1,4,7。他去1的位置,發現id為1的已經佔了這個位置,那他會把id為1的擠走,自己佔領。這個時候id為1的元素被擠走了之後會去找他第二個巢穴,就是3這個位置。如果3位置有其他元素,那這個元素也會被擠走,找他的其他巢穴。
思想設計還可以,落地實現沒有。
快取穿透總結:
我來寫直接用解決方案1,加快取裡面,這個如果是springboot2.x+spring3.1之後,而且引入了redis,那麼加一個註解@Cacheable("menu") ,把結果快取起來。
開啟spring-cache配置,快取結果為null的資料。
spring.cache.redis.cache-null-values=true
切記理論是一回事,落地實現是另一回事,主要還是看公司。
再提一點,可以對惡意請求的ip加入黑名單,那就是Gatway的操作了,這裡不做闡述。