快取穿透、雪崩、熱點與Redis

sorra發表於2017-04-03

向大家推薦這篇文章——Redis架構之防雪崩設計:網站不當機背後的兵法

(另外推薦我去年的短文作為餐前點心——略談服務端快取設計

《Redis架構之防雪崩設計》這篇文章(下文稱之為“原文”)寫得非常好,全面概括了大規模系統可能面對的快取穿透和快取雪崩等問題,可以看出是一線實戰經驗的精華總結,非常適合大家學習。

而我想再補充一些資訊,使“原文”的版圖更加完整。

關於“快取穿透”

“原文”給出了空物件和布隆過濾器兩種解決方案。

空物件是首選方案,簡單直接,碰到查詢結果為空的鍵,放一個空值在快取中,下次再訪問就立刻知道這個鍵無效,不用發出SQL了。但“原文”也說了,存在如下問題:

第一,空值做了快取,意味著快取層中存了更多的鍵,需要更多的記憶體空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類資料設定一個較短的過期時間,讓其自動剔除。

第二,快取層和儲存層的資料會有一段時間視窗的不一致,可能會對業務有一定影響。例如過期時間設定為 5 分鐘,如果此時儲存層新增了這個資料,那此段時間就會出現快取層和儲存層資料的不一致,此時可以利用訊息系統或者其他方式清除掉快取層中的空物件。

對於第一點,我還建議空值放在另外的快取空間中,不宜與正常值共用空間,否則當空間不足時,快取系統的LRU演算法可能會先剔除正常值,再剔除空值——這個漏洞可能會受到攻擊。

對於第二點,如果是Redis快取,更新資料後直接在Redis中清除即可;如果是本地快取,就需要用訊息來通知其他機器清除各自的本地快取了。(業界終於接受了用訊息來同步快取的設計思想,cheers! )我有一個小專案joint-cache-redis來簡單地演示“用訊息來同步多個機器的快取”,而且在實踐中發現Kafka可能比Redis MQ更適合於這個場景。

關於“快取雪崩”

這句概括很傳神!快取層宕掉後,流量會像奔逃的野牛一樣,打向後端儲存

沒什麼要補充的,就感謝一下Netflix開源的Hystrix吧!雖然只是一個庫,但是要實現可靠的限流演算法還是頗有門道的。

關於“快取熱點 key 重建”

“原文”說到在快取失效的瞬間,有大量執行緒來重建快取,造成後端負載加大,甚至可能會讓應用崩潰,並給出“互斥鎖”和“永遠不過期”兩種候選方案。

互斥鎖(Mutex):

“分散式快取加鎖”通常是一個反模式(見我去年的文章大型服務端開發的反模式第7條),如果持有鎖的例項不穩定導致沒及時釋放,就會浪費這個鎖,直到鎖過期。“原文”的作者還指出有死鎖的風險。

其實是可以優化的:等待一兩次後,重試時可繞過互斥鎖。即使繞過互斥鎖,也不會產生什麼不好的後果,因為更新快取是一個冪等操作。

也可以把鎖的過期時間設得更短。

從這個例子我們能感覺到,冪等操作比非冪等操作更容易優化。

永遠不過期:

“原文”很好地介紹了在Redis中的做法。對於Guava本地快取就簡單多了,使用refreshAfterWrite即可。

“原文”讀到最後,才知道這是《Redis開發與運維》一書的節選,相信這本書會是國產技術書籍的精品!

相關文章