緩存穿透、雪崩、熱點與Redis
向大家推薦這篇文章——Redis架構之防雪崩設計:網站不宕機背後的兵法
(另外推薦我去年的短文作為餐前點心——略談服務端緩存設計)
《Redis架構之防雪崩設計》這篇文章(下文稱之為「原文」)寫得非常好,全面概括了大規模系統可能面對的緩存穿透和緩存雪崩等問題,可以看出是一線實戰經驗的精華總結,非常適合大家學習。
而我想再補充一些信息,使「原文」的版圖更加完整。
關於「緩存穿透」
「原文」給出了空對象和布隆過濾器兩種解決方案。
空對象是首選方案,簡單直接,碰到查詢結果為空的鍵,放一個空值在緩存中,下次再訪問就立刻知道這個鍵無效,不用發出SQL了。但「原文」也說了,存在如下問題:
第一,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內存空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類數據設置一個較短的過期時間,讓其自動剔除。
第二,緩存層和存儲層的數據會有一段時間窗口的不一致,可能會對業務有一定影響。例如過期時間設置為 5 分鐘,如果此時存儲層添加了這個數據,那此段時間就會出現緩存層和存儲層數據的不一致,此時可以利用消息系統或者其他方式清除掉緩存層中的空對象。
對於第一點,我還建議空值放在另外的緩存空間中,不宜與正常值共用空間,否則當空間不足時,緩存系統的LRU演算法可能會先剔除正常值,再剔除空值——這個漏洞可能會受到攻擊。
對於第二點,如果是Redis緩存,更新數據後直接在Redis中清除即可;如果是本地緩存,就需要用消息來通知其他機器清除各自的本地緩存了。(業界終於接受了用消息來同步緩存的設計思想,cheers! )我有一個小項目joint-cache-redis來簡單地演示「用消息來同步多個機器的緩存」,而且在實踐中發現Kafka可能比Redis更適合於這個場景。
關於「緩存雪崩」
這句概括很傳神!緩存層宕掉後,流量會像奔逃的野牛一樣,打向後端存儲
沒什麼要補充的,就感謝一下Netflix開源的Hystrix吧!雖然只是一個庫,但是要實現可靠的限流演算法還是頗有門道的。
關於「緩存熱點 key 重建」
「原文」說到在緩存失效的瞬間,有大量線程來重建緩存,造成後端負載加大,甚至可能會讓應用崩潰,並給出「互斥鎖」和「永遠不過期」兩種候選方案。
互斥鎖(Mutex):
「分散式緩存加鎖」通常是一個反模式(見我去年的文章大型服務端開發的反模式第7條),如果持有鎖的實例不穩定導致沒及時釋放,就會浪費這個鎖,直到鎖過期。「原文」的作者還指出有死鎖的風險。
其實是可以優化的:等待一兩次後,重試時可繞過互斥鎖。即使繞過互斥鎖,也不會產生什麼不好的後果,因為更新緩存是一個冪等操作。
也可以把鎖的過期時間設得更短。
從這個例子我們能感覺到,冪等操作比非冪等操作更容易優化。
永遠不過期:
「原文"很好地介紹了在Redis中的做法。對於Guava本地緩存就簡單多了,使用refreshAfterWrite即可。
「原文」讀到最後,才知道這是《Redis開發與運維》一書的節選,相信這本書會是國產技術書籍的精品!
推薦閱讀:
※200G的數據,主要是查詢操作,酷睿I5個人PC,應該選擇什麼資料庫來存儲?
※Spring Boot使用Redis進行消息的發布訂閱
※如何評價360開源的pika項目?
※集群環境中資料庫與緩存的三板斧
※Redis源碼剖析--源碼結構解析