快取Bigkey堅決不要用,拆分是王道

架構擺渡人發表於2021-12-25

大家好,我是架構擺渡人。這是實踐經驗系列的第四篇文章,這個系列會給大家分享很多在實際工作中有用的經驗,如果有收穫,還請分享給更多的朋友。

背景介紹

在高併發的業務場景中,快取是必須要上的,用來扛高併發。在某個業務場景中,增加了對一個配置資訊的快取,最開始是直接讀取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,根據業務場景進行優化。同時在後續使用快取的場景對快取內容嚴格把關,防止出現類似的問題。

相關文章