標籤:

緩存穿透、雪崩、熱點與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源碼剖析--源碼結構解析

TAG:Redis | 缓存 |