緩存擊穿
緩存擊穿,是指一個key非常熱點,在不停的扛着大並發,大並發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大並發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。
比如在做電商項目的時候,把這貨就成為“爆款”。
其實,大多數情況下這種爆款很難對數據庫服務器造成壓垮性的壓力。達到這個級別的公司沒有幾家的。所以,本着務實主義,對主打商品都是早早的做好了准備,讓緩存永不過期。即便某些商品自己發酵成了爆款,也是直接設為永不過期就好了。
其他方式:
1. 使用互斥鎖(mutex key): 這種解決方案思路比較簡單,就是只讓一個線程構建緩存,其他線程等待構建緩存的線程執行完,重新從緩存獲取數據就可以了。
如果是分布式鎖,就是用分布式鎖來處理就好了。
2. "提前"使用互斥鎖(mutex key):
在value內部設置1個超時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發現它已經過期時候,馬上延長timeout1並重新設置到cache。然后再從數據庫加載數據並設置到cache中。
3. "永遠不過期":
這里的“永遠不過期”包含兩層意思:
(1) 從redis上看,確實沒有設置過期時間,這就保證了,不會出現熱點key過期問題,也就是“物理”不過期。
(2) 從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value里,如果發現要過期了,通過一個后台的異步線程進行緩存的構建,也就是“邏輯”過期
從實戰看,這種方法對於性能非常友好,唯一不足的就是構建緩存時候,其余線程(非構建緩存的線程)可能訪問的是老數據,但是對於一般的互聯網功能來說這個還是可以忍受。
4. 資源保護:
可以做資源的隔離保護主線程池,如果把這個應用到緩存的構建也未嘗不可。
總結
1. 熱點key + 過期時間 + 復雜的構建緩存過程 => mutex key問題
2. 構建緩存一個線程做就可以了。
3. 四種解決方案:沒有最佳只有最合適。