你們好!我是萬俊峯,go-zero 做者。感謝 ArchSummit 提供這麼好的機會來跟你們分享一下go-zero的緩存最佳實踐。git
首先,你們能夠想想:咱們在流量激增的狀況下,服務端哪一個部分最有可能會是第一個瓶頸?我相信大部分人遇到的都會是數據庫首先扛不住,量一塊兒來,數據庫慢查詢,甚至卡死。此時,上層服務有怎麼強的治理能力都是無濟於事的。github
因此咱們常說看一個系統架構設計的好很差,不少時候看看緩存設計的如何就知道了。咱們曾經遇到過這樣的問題,在我加入以前,咱們的服務是沒有緩存的,雖然當時流量還不算高,可是天天到流量高峯時間段,你們就會特別緊張,一週宕機好幾次,數據庫直接被打死,而後啥也幹不了,只能重啓;我當時仍是顧問,看了看系統設計,只能救急,就讓你們先加上了緩存,可是因爲你們對緩存的認知不夠以及老系統的混亂,每一個業務開發人員都會按照本身的方式來手撕緩存。這樣致使的問題就是緩存用了,可是數據七零八落,壓根沒有辦法保證數據的一致性。這確實是一個比較痛苦的經歷,應該能引發你們的共鳴和回憶。數據庫
而後我把整個系統推倒從新設計了,其中緩存部分的架構設計在其中做用很是明顯,因而有了今天的分享。緩存
我主要分爲如下幾個部分跟你們探討:微信
緩存系統涉及的問題和知識點是比較多的,我分爲如下幾個方面來討論:架構
因爲篇幅太長,本文做爲系列文章第一篇,主要跟你們探討『緩存系統穩定性』併發
緩存穩定性方面,網上基本全部的緩存相關文章和分享都會講到三個重點:框架
爲何首先講緩存穩定性呢?你們能夠回憶一下,咱們什麼時候會引入緩存?通常都是當DB有壓力,甚至常常被打掛的狀況下才會引入緩存,因此咱們首先就是爲了解決穩定性的問題而引入緩存系統的。分佈式
緩存穿透存在的緣由是請求不存在的數據,從圖中咱們能夠看到對同一個數據的請求1會先去訪問緩存,可是由於數據不存在,因此緩存裏確定沒有,那麼就落到DB去了,對同一個數據的請求二、請求3也一樣會透過緩存落到DB去,這樣當大量請求不存在的數據時DB壓力就會特別大,尤爲是可能會惡意請求打垮(不懷好意的人發現一個數據不存在,而後就大量發起對這個不存在數據的請求)。微服務
go-zero 的解決方法是:對於不存在的數據的請求咱們也會在緩存裏短暫(好比一分鐘)存放一個佔位符,這樣對同一個不存在數據的DB請求數就會跟實際請求數解耦了,固然在業務側也能夠在新增數據時刪除該佔位符以確保新增數據能夠馬上查詢到。
緩存擊穿的緣由是熱點數據的過時,由於是熱點數據,因此一旦過時可能就會有大量對該熱點數據的請求同時過來,這時若是全部請求在緩存裏都找不到數據,若是同時落到DB去的話,那麼DB就會瞬間承受巨大的壓力,甚至直接卡死。
go-zero 的解決方法是:對於相同的數據咱們能夠藉助於 core/syncx/SharedCalls
來確保同一時間只有一個請求落到DB,對同一個數據的其它請求等待第一個請求返回並共享結果或錯誤,根據不一樣的併發場景,咱們能夠選擇使用進程內的鎖(併發量不是很是高),或者分佈式鎖(併發量很高)。若是不是特別須要,咱們通常推薦進程內的鎖便可,畢竟引入分佈式鎖會增長複雜度和成本,借鑑奧卡姆剃刀理論:如非必要,勿增實體。
咱們來一塊兒看一下上圖緩存擊穿防禦流程,咱們用不一樣顏色表示不一樣請求:
緩存雪崩的緣由是大量同時加載的緩存有相同的過時時間,在過時時間到達的時候出現短期內大量緩存過時,這樣就會讓不少請求同時落到DB去,從而使DB壓力激增,甚至卡死。
好比疫情下在線教學場景,高中、初中、小學是分幾個時間段同時開課的,那麼這時就會有大量數據同時加載,而且設置了相同的過時時間,在過時時間到達的時候就會對等出現一個一個的DB請求波峯,這樣的壓力波峯會傳遞到下一個週期,甚至出現疊加。
go-zero 的解決方法是:
咱們作個實驗,若是用1萬個數據,過時時間設爲1小時,標準誤差設爲5%,那麼過時時間會比較均勻的分佈在3400~3800秒之間。若是咱們的默認過時時間是7天,那麼就會均勻分佈在以7天爲中心點的16小時內。這樣就能夠很好的防止了緩存的雪崩問題。
本文跟你們一塊兒討論了緩存系統的常見穩定性問題,下一篇我來跟你們一塊兒分析緩存的數據一致性問題。
全部這些問題的解決方法都已包含在 go-zero 微服務框架裏,若是你想要更好的瞭解 go-zero 項目,歡迎前往官方網站上學習具體的示例。
https://github.com/tal-tech/go-zero
歡迎使用 go-zero 並 star 支持咱們!
關注『微服務實踐』公衆號並點擊 進羣 獲取社區羣二維碼。
go-zero 系列文章見『微服務實踐』公衆號