大家好,我是架構擺渡人。這是實踐經驗系列的第四篇文章,這個系列會給大家分享很多在實際工作中有用的經驗,如果有收穫,還請分享給更多的朋友。
背景介紹
在高併發的業務場景中,快取是必須要上的,用來扛高併發。在某個業務場景中,增加了對一個配置資訊的快取,最開始是直接讀取DB的,為了效能考慮在前面加了一層快取。
加完後很長一段時間也沒問題,DB的壓力也減小了很多。不幸的是在某天的一個時間點內,流量增加了好幾倍,RT直線上升,介面各種超時,就這樣,一個線上故障誕生了。
整個過程持續了1分鐘左右,監控告警稍微有點延遲,剛看完監控告警,準備介入處理時流量已經跌下來了,介面也恢復了正常。
事後,通過監控發現介面超時的原因是因為底層Redis超時了,說到這可能大家都抱著懷疑的態度,Redis這麼快也能超時?是的,你沒看錯,就是Redis超時了。
而Redis超時的原因並不是說Redis效能不行,而是在同一時刻有大量的請求訪問了同一個Key,這個Key快取的內容很大,導致一瞬間就把網路頻寬給佔用完了,後續請求都進不來。
解決方案
擴容頻寬
直接擴容是最簡單有效的方式,如果有持續的高流量造成了影響,緊急擴容是必須要走的,先解決當前問題。
等流量平穩後再考慮程式碼層面的改造,因為頻寬也不是無限的,程式不處理好,始終是個風險點。
拆分BigKey
資料量大了,我們會分庫分表。耦合嚴重了,我們會拆新的模組或者獨立的服務。小組人多了,我們會拆分成多個組。遇到BigKey,那就是拆它拆它拆它。
假設這是你的快取內容,裡面是一個很大的Json字串,儲存的是配置資訊:
{
"a":"....",
"b":"....",
"c":"...."
.........
}
那麼可以把物件中的每個Key再拆分一次,作為一個獨立的Key,這樣它的Value就小了很多。不同的key會在叢集中的不同節點上,也就不會出現集中訪問某個節點,頻寬不夠的場景。
當然我這邊說的是String型別,如果你的是List, Set這種,其實原理是一樣的,同樣是拆分成多個小的List, Key的字首或者字尾不一樣即可。
本地快取
本地快取,也是應對熱點Key的常用解決方案。大家想想,一個固定的Key儲存在Redis中,然後儲存的內容也很大,訪問量也比較高,頻寬很容易成為瓶頸,因為要遠端訪問獲取快取內容。
如果將快取儲存在本地,那麼就可以不用遠端訪問,從而頻寬也就不會成為瓶頸。
如果用了本地快取,相信很多讀者第一想法就是一致性怎麼維護,這個無論是在實際應用中還是面試中都是一個高頻的問題。
還是得從業務場景觸發,用快取的場景肯定就是沒有強一致性的要求,能夠容忍短暫的不一致。所以在Redis快取失效或者資料有變更的時候,本地快取也需要同步清除。
一般都會採用訊息廣播的方式進行通知本地快取失效,因為服務是叢集部署的,每個節點上都有一份快取資料,所以需要廣播通知。
BigKey的治理
最後要進行BigKey的治理,梳理出來目前已有的BigKey,根據業務場景進行優化。同時在後續使用快取的場景對快取內容嚴格把關,防止出現類似的問題。