天天看點

聊聊Redis的資料熱點問題

  前兩天,我們使用的某雲廠商服務挂了,而且一挂就是挂大半天,我們的服務強依賴于他們,是以我們也跟着一起挂。然而我們卻無能為力,隻能等他們恢複。事故原因中聽他們提到Redis有個熱key,正好我在上家公司負責過部門Redis叢集,也處理過很多起Redis資料熱點的問題,接下來就一起聊聊什麼是Redis熱點?Redis熱點問題為什麼會極大地影響整個叢集的性能?如何避免Redis資料熱點?熱點問題如何排查?熱點問題如何解決?

什麼是Redis熱點?

  我曾在我過往的博文中多次提到了局部性一詞(關于局部性可以看下我之前的博文 ​​局部性原理​​),資料熱點就是資料通路局部性的展現,具體表現就是Redis中某個Key的通路頻次遠大于其他剩餘的Key,我們也有一句俗語來形容這種現象旱的旱死,澇的澇死。

聊聊Redis的資料熱點問題

  為什麼Redis會有熱點問題?這就得從Redis的原理說起了。衆所周知,Redis中存儲的是K-V資料,在叢集模式下,Redis會将所有資料按Key的CRC64值配置設定到16384個資料槽(slot)中,并将這16384個資料槽配置設定到叢集中各個機器上,盡可能實作資料在各個機器上的均勻存儲。但均勻存儲并不意味着均勻通路,有時候某個Key的請求會占到總請求的很大一部分,這就會導緻請求集中在某個Redis執行個體上,将該Redis執行個體的承載能力耗盡,所有存儲在這個執行個體上的其他資料也就無法正常通路了,這就意味着所有依賴于這些資料的服務都會出問題。

  這裡的出問題并不是說Redis會直接當機,衆所周知Redis的核心流程是單線程模式,這就意味着Redis是串行處理所有請求的,當請求過多時,請求就會擁堵起來,從應用層的視角來看,就是請求Redis的耗時會特别特别高。因為應用層使用Redis都是作為緩存,都是同步請求,是以會間接導緻應用層的請求處理也會特别耗時,進而導緻應用層請求也逐漸擁堵,最終整體不可用。

  我們來舉個簡單的例子,相信大家都在微網誌上吃過瓜,當有大瓜出現時,微網誌上會迅速湧入一批使用者檢索相關資訊,瘋狂通路同一份微網誌(資料),這種情況下這份資料就是熱點資料,如果資料太“熱”最終會導緻微網誌挂掉,實際上微網誌挂掉的情況已經出現很多次了,不是因為微網誌技術不行,而是因為熱點問題太可怕。

Redis熱點是如何拖垮其他服務的?

  Redis的熱點并不僅僅會導緻單個服務異常,而是會導緻所有依賴于此Redis叢集的所有服務異常。上圖中Server2、Server、Server3頻繁通路XXX_KEY,導緻RedisServer2執行個體不可用,因為Server4依賴于RedisServer2上的Key7,即便是RedisServer1 3 4 5 都正常服務,Server4也無法對外提供正常的服務。

聊聊Redis的資料熱點問題

  是不是有人會問RedisServer2挂了,不能把它下掉,換一個新的機器上去嗎? 其實在Redis叢集模式下,某個執行個體當機Redis叢集會自動将其替換。但現在的情況是,即便是替換了新執行個體,大量的請求也會一下子湧進來将其壓挂。是以面對Redis熱點問題,重新開機之類的手段是無效的,隻能從請求端解決問題。  其實這就是一個很明顯的 木桶模型, 一個木桶所能裝水的多少取決于木桶上最短的那塊木闆,當應用使用Redis叢集時,Redis叢集的性能上限并不完全等于單執行個體上限乘以執行個體個數。 但當Redis叢集中任一執行個體有問題,上層感覺到的就是整個Redis叢集有問題。

聊聊Redis的資料熱點問題

Redis熱點如何避免?

  上文也說到,熱點問題其實是局部性問題,而局部性問題的避免其實非常難,任何分布式系統幾乎都會受局部性的影響。面對這種問題,說實話沒有絕對可以避免的方式,隻能提前通過分析資料的特性,做好相應的措施。說白了就是靠經驗,不知道大家有啥其他好的思路,可以在評論區探讨下。

熱點問題如何排查?

  Redis的熱點的問題其實算是很好查的,就是靠監控資料,監控Redis各個執行個體的CPU使用率、QPS 資料,如果你看到redis叢集中某些執行個體負載和QPS特别高,但其他執行個體負載很低,不用問肯定是出現熱點問題了,接下來你需要做的就是找出具體的熱點key,并且找出資料通路的來源。

  找出熱點Key其實也很簡單,抓部分通路日志,然後統計下很容易就看出來了。但比較難的是找出資料通路的來源,像我之前所在的公司,同一個Redis叢集是被很多業務所共享的,但Redis的通路并為被納入到全鍊路監控的資料中,是以找出通路來源最直接的方式就是在群裡面問,聽起挺原始的,但也沒有其他方法。

熱點問題如何解決?

  雖然解決問題最好的方式是避免問題的發生,但我剛才也說了,Redis熱點其實很難避免,任何業務中熱點一定有,但不一定會造成災難而已。 熱點的發現不一定非得是事故引出的,我們也可以在日常工作中定期巡檢,一有發現熱點的苗頭,直接将其扼殺。

對于發現熱點後如何解決,我這裡提供兩個我的解決思路,大家可以探讨下:

應用層Cache

聊聊Redis的資料熱點問題

  常用的實作方式就是在應用裡實作本地緩存(LocalCache),就相當于是對Redis資料又加了一層Cache,對于那些非常熱的熱點資料,應用層有極大的機率能在本地緩存中找到資料,隻有極小部分LocalCache資料過期時的請求會漏到下面,這樣熱點資料的請求在應用層内部就能消化,進而極大減少對Redis的壓力。

  這種實作方法的話,僅需要資料讀取端做改造,資料寫入端完全不需要改造。然而缺點的話也很明顯:

  1. 需要各端自行實作,會增加應用層開發和維護成本。
  2. 會額外浪費各端的存儲空間。
  3. 需要針對性開發,不适合大範圍推廣。

增加資料副本

  既然熱點問題是因為某個Key被大量通路導緻的,那我們将這個Key的請求做下拆分不就行了。例如,原始的熱點Key叫做XXX_KEY,我們在資料寫入的時候,可以用不同的Key重複寫10份,比如 XXX_KEY_01, XXX_KEY_02……XXX_KEY_10, 通路的時候在原始Key上随機憑接一個1-10之間的字尾即可,這樣就能實作資料請求的分散,如果想讓請求更分散,可以存儲更多的副本。

聊聊Redis的資料熱點問題

  這種方案的優點就是資料讀取端實作成本較低(也不是完全沒有),但對資料寫入端的要求就高多了。不僅要寫入多份,而且還得考慮資料寫入後一緻性的問題。這種方法要求兩端都得改,貌似更麻煩了,你是不是覺得還不如第一種方案?實則不然,一般來說讀取端會很多且很分散,改造的成本會非常高,頻繁變動更是不太可能,是以有些工作不得不放置到比較集中的端上。

  以上兩種方案其實都是通過存儲來換性能,主要差異點就在于由誰來做而已。前者是用戶端來做,後者是服務端來做,各有優缺點。 有沒有可能對外提供純粹的Redis協定,但可以解決資料熱點的問題?。魯迅……David Wheeler曾經說過,計算機科學的如何問題都可以通過增加一層來解決,熱點的問題也不例外。我們可以在應用和Redis之間加一層中間層,這層中間層可以是真實的服務,比如資料統一通路層。也可以是特制的Redis用戶端。