常見緩存問題處理-緩存熱點key


參考:

https://blog.csdn.net/shipfei_csdn/article/details/103380618

https://www.cnblogs.com/rjzheng/p/10874537.html

 

 

 

緩存熱點key的處理探討

使用緩存集群的時候,最怕的就是熱key、大value這兩種問題。熱key問題,指的就是緩存集群中的某個key在瞬間被數萬甚至十萬的並發請求打爆。大value問題,指的是某個key對應的value可能有gb級別的大小,導致查詢value的時候會引發網絡相關的故障問題。這里說一下熱key問題。

為什么要使用緩存集群

簡單來說,假設你手頭上有個系統,它本身是集群部署的,然后后面有一套緩存集群,這個集群不管你用Redis Cluster,還是Memcached,或者是公司自研緩存集群,都可以。

那么這套系統用緩存集群做什么呢?很簡單,在緩存放一些平時不怎么變動的數據,然后用戶在查詢大量的、平時不怎么變動的數據的時候,就可以直接訪問緩存而不需要訪問數據庫了。緩存集群的並發能力很強,而且讀緩存的性能也很高。舉個例子,假設每秒鍾有2萬的請求,但是其中的90%都是讀請求,那么每秒鍾1.8萬的請求都是在讀一些不太變化的數據,而不是寫數據。此時,如果你把數據都放在數據庫里,然后每秒鍾發送2萬個請求到數據庫上讀寫數據,顯然是不合適的,因為如果要數據庫能承載每秒2萬個請求的話,很可能就需要搞分庫分表+讀寫分離。比如,你需要分出來3個主庫去承載每秒2000的寫入請求,然后每個主庫掛3個從庫,一個9個從庫去承載每秒1.8萬的讀請求。那么這樣你就需要一共是12台高配置的數據庫服務器,成本非常高,而且很不合適。

因此,此時你就可以完全把平時不太變化的數據放在緩存集群里,緩存集群可以采用2主2從,主節點用來寫入緩存,從節點用來讀取緩存。以緩存集群的性能,2個從節點完全可以用來承載每秒1.8萬的大量讀,然后3個主庫就只要承載每秒2000的寫請求和少量的其他讀就可以了。這樣,耗費的機器瞬間變成了4台緩存機器+3台數據庫機器=7台機器,比起前面的12台機器減少了很大的資源開銷。事實上,緩存是系統架構里非常重要的一個組成部分。很多的時候,對於哪些很少變化但是大量高並發讀的數據,通過緩存集群來抗高並發讀,是非常合適的。

以上的機器數量、並發請求量只是一個簡單的示例,實際的情況要復雜得多,通過這個例子就能大概理解在系統中為什么要使用緩存集群來承載讀寫請求。

20萬用戶同時訪問一個熱點緩存的問題

做一個假設,現在有10個緩存節點來抗大量的讀請求。正常情況下,讀請求應該是均勻地落在10個緩存節點上的(負載均衡),那么這10個緩存節點,每秒承載1萬個請求是差不多的。然后我們來做一個假設,一個節點承載2萬個請求是極限,所以一般就限制一個節點正常承載1萬個請求就ok了,因為要稍微留一些buffer出來。所謂的熱點緩存問題,就是突然因為莫名的原因,出現大量的用戶訪問同一條緩存數據。舉個例子就是,某個明星突然宣布跟某某結婚,這個時候就可能會引發短時間內每秒有數十萬的用戶去查看這條結婚的新聞。假設這條新聞是一個緩存,然后對應就是一個緩存key,就存在一台緩存機器上,假設這時候有20萬個請求一起奔向這台緩存機器上的一個緩存key上,就會引發熱點緩存問題。

通過上圖就很明顯看出問題來了。我們剛才假設的是一個緩存Slave節點最多每秒接受2萬的請求(當然了,實際的緩存單機承載5萬~10萬的都請求也是可能的),此時每秒卻突然奔過來20萬的請求到這台機器上,那么這台機器就會被這20萬請求弄宕機。一旦緩存集群開始出現機器的宕機,那么讀請求發現讀不到數據,就會從數據庫里面去提取原始數據,然后將這些數據放到剩余的其他緩存機器里面去。但是接踵而來的每秒20萬的請求還是會接着壓垮其他的緩存機器,周而復始,最終導致緩存集群全盤奔潰,引發系統的整體宕機。

基於流式計算技術的緩存熱點自動發現

這里關鍵的一點,就是對於這種熱點緩存,系統需要能夠在熱點緩存突然發生的時候,直接發現它,然后瞬間立馬實現毫秒級的自動負載均衡。那么如何實現自動發現熱點緩存問題呢?首先,一般出現緩存熱點的時候,每秒並發肯定是很高的,可能每秒都幾十萬甚至上百萬的請求量過來,多是可能的。所以此時完全可以基於大數據領域的流式計算技術來進行實時數據訪問次數的統計,比如storm、spark streaming或flink,這些技術都是可以的。然后一旦在實時數據訪問次數統計的過程中,比如發現一秒之內,某條數據突然訪問次數超過了1000,就直接立刻把這條數據判定為是熱點數據,可以將這個發現出來的熱點數據寫入比如zookeeper中。當然,系統如何判定熱點數據可以根據自己的業務還有經驗值來。

那么流式計算系統在進行數據訪問次數統計的時候,會不會也存在說單台機器被請求每秒幾十萬次的問題呢?答案是否,因為流式計算技術,尤其是storm這種系統,可以做到同一條數據的請求過來,先分散在很多機器里進行本地計算,最后再匯總局部計算結果到一台機器進行全局匯總。所以幾十萬的請求可以先分散在比如100台機器上,每台機器統計了這條數據的幾千次請求。然后100條局部計算好的結果匯總到一台機器做全局計算即可,所以基於流式計算技術來進行統計是不會有熱點問題的。

熱點緩存自動加載為JVM本地緩存

現在系統可以對zookeeper指定的熱點緩存對應的znode進行監聽了,如果有變化,系統立馬就感知到了。這時,系統層就可以立馬把相關的緩存數據從數據庫加載出來,然后直接放在自己系統內部的本地緩存即可。這個本地緩存,用ehcach,hashmap都可以,具體看業務需求,主要就是要將緩存集群里的集中式緩存直接變成每個系統自己本地實現的緩存即可,每個系統自己本地是無法緩存過多數據的。因為一般這種普通系統單實例,部署機器可能就是一個4核8G的機器,留給本地緩存的空間是很少的,所以用來放這種熱點數據的本地緩存是最合適的。

假設系統層集群部署了100台機器,這時這100台機器瞬間在本地都會有一份熱點緩存的副本。然后接下來對熱點緩存的讀操作,直接系統本地緩存都出來就會返回了,不需要再走緩存集群了。這樣的話,也不可能允許每秒20萬的讀請求到達緩存機器的一台機器上讀一個熱點緩存了,而是變成100台機器每台機器承載數千個請求,這數千請求直接從機器的本地緩存返回數據。

限流熔斷保護

除此之外,在每個系統內部,其實還應該專門加一個對熱點數據訪問的限流熔斷保護措施。每個系統實例的內部,都可以加一個熔斷保護機制,假設緩存集群最多每秒承載4萬讀請求,那么一共有100個系統實例。這時候就要限制好,每個系統實例每秒最多請求緩存集群讀操作不超過400次,一超過就可以熔斷掉,不讓請求緩存集群,直接返回一個空白信息,然后用戶稍后會自行再次重新刷新頁面之類的。通過系統層自己直接加限流熔斷保護措施,就可以很好地保護后面的緩存集群、數據庫集群之類的不會被打死。

總結

具體要不要在系統里面實現這種復雜的緩存熱點優化架構,要看系統有沒有這種場景。如果系統有熱點緩存問題,那么就要實現類似的復雜熱點緩存支撐架構。但是如果沒有的話,也別過度設計,系統可能並不需要這么復雜的架構,反而會成為拖累。

 

"人這一生,都是命。你的稟賦就決定了你90分的人生。別人的幫助可以決定9分,你自己的努力只占1分。但是只為了這1分,你就要拼盡全力,因為這1分是你唯一能夠爭取的,這1分彌足珍貴。"

 

 

 

 

 

 

【原創】談談redis的熱key問題如何解決

引言

講了幾天的數據庫系列的文章,大家一定看煩了,其實還沒講完。。。(以下省略一萬字)。
今天我們換換口味,來寫redis方面的內容,談談熱key問題如何解決。
其實熱key問題說來也很簡單,就是瞬間有幾十萬的請求去訪問redis上某個固定的key,從而壓垮緩存服務的情情況。
其實生活中也是有不少這樣的例子。比如XX明星結婚。那么關於XX明星的Key就會瞬間增大,就會出現熱數據問題。
ps:hot key和big key問題,大家一定要有所了解。
本文預計分為如下幾個部分

  • 熱key問題
  • 如何發現
  • 業內方案

正文

熱Key問題

上面提到,所謂熱key問題就是,突然有幾十萬的請求去訪問redis上的某個特定key。那么,這樣會造成流量過於集中,達到物理網卡上限,從而導致這台redis的服務器宕機。
那接下來這個key的請求,就會直接懟到你的數據庫上,導致你的服務不可用。

怎么發現熱key

方法一:憑借業務經驗,進行預估哪些是熱key
其實這個方法還是挺有可行性的。比如某商品在做秒殺,那這個商品的key就可以判斷出是熱key。缺點很明顯,並非所有業務都能預估出哪些key是熱key。
方法二:在客戶端進行收集
這個方式就是在操作redis之前,加入一行代碼進行數據統計。那么這個數據統計的方式有很多種,也可以是給外部的通訊系統發送一個通知信息。缺點就是對客戶端代碼造成入侵。
方法三:在Proxy層做收集
有些集群架構是下面這樣的,Proxy可以是Twemproxy,是統一的入口。可以在Proxy層做收集上報,但是缺點很明顯,並非所有的redis集群架構都有proxy。

方法四:用redis自帶命令
(1)monitor命令,該命令可以實時抓取出redis服務器接收到的命令,然后寫代碼統計出熱key是啥。當然,也有現成的分析工具可以給你使用,比如redis-faina。但是該命令在高並發的條件下,有內存增暴增的隱患,還會降低redis的性能。
(2)hotkeys參數,redis 4.0.3提供了redis-cli的熱點key發現功能,執行redis-cli時加上–hotkeys選項即可。但是該參數在執行的時候,如果key比較多,執行起來比較慢。
方法五:自己抓包評估
Redis客戶端使用TCP協議與服務端進行交互,通信協議采用的是RESP。自己寫程序監聽端口,按照RESP協議規則解析數據,進行分析。缺點就是開發成本高,維護困難,有丟包可能性。

以上五種方案,各有優缺點。根據自己業務場景進行抉擇即可。那么發現熱key后,如何解決呢?

如何解決

目前業內的方案有兩種
(1)利用二級緩存
比如利用ehcache,或者一個HashMap都可以。在你發現熱key以后,把熱key加載到系統的JVM中。
針對這種熱key請求,會直接從jvm中取,而不會走到redis層。
假設此時有十萬個針對同一個key的請求過來,如果沒有本地緩存,這十萬個請求就直接懟到同一台redis上了。
現在假設,你的應用層有50台機器,OK,你也有jvm緩存了。這十萬個請求平均分散開來,每個機器有2000個請求,會從JVM中取到value值,然后返回數據。避免了十萬個請求懟到同一台redis上的情形。
(2)備份熱key
這個方案也很簡單。不要讓key走到同一台redis上不就行了。我們把這個key,在多個redis上都存一份不就好了。接下來,有熱key請求進來的時候,我們就在有備份的redis上隨機選取一台,進行訪問取值,返回數據。
假設redis的集群數量為N,步驟如下圖所示

注:不一定是2N,你想取3N,4N都可以,看要求。
偽代碼如下

const M = N * 2 //生成隨機數 random = GenRandom(0, M) //構造備份新key bakHotKey = hotKey + “_” + random data = redis.GET(bakHotKey) if data == NULL { data = GetFromDB() redis.SET(bakHotKey, expireTime + GenRandom(0,5)) } 

業內方案

OK,其實看完上面的內容,大家可能會有一個疑問。

煙哥,有辦法在項目運行過程中,自動發現熱key,然后程序自動處理么?

嗯,好問題,那我們來講講業內怎么做的。其實只有兩步
(1)監控熱key
(2)通知系統做處理
正巧,前幾天有贊出了一篇《有贊透明多級緩存解決方案(TMC)》,里頭也有提到熱點key問題,我們剛好借此說明
(1)監控熱key
在監控熱key方面,有贊用的是方式二:在客戶端進行收集
在《有贊透明多級緩存解決方案(TMC)》中有一句話提到

TMC 對原生jedis包的JedisPool和Jedis類做了改造,在JedisPool初始化過程中集成TMC“熱點發現”+“本地緩存”功能Hermes-SDK包的初始化邏輯。

也就說人家改寫了jedis原生的jar包,加入了Hermes-SDK包。
那Hermes-SDK包用來干嘛?
OK,就是做熱點發現本地緩存
從監控的角度看,該包對於Jedis-Client的每次key值訪問請求,Hermes-SDK 都會通過其通信模塊將key訪問事件異步上報給Hermes服務端集群,以便其根據上報數據進行“熱點探測”。

當然,這只是其中一種方式,有的公司在監控方面用的是方式五:自己抓包評估
具體是這么做的,先利用flink搭建一套流式計算系統。然后自己寫一個抓包程序抓redis監聽端口的數據,抓到數據后往kafka里丟。
接下來,流式計算系統消費kafka里的數據,進行數據統計即可,也能達到監控熱key的目的。

(2)通知系統做處理
在這個角度,有贊用的是上面的解決方案一:利用二級緩存進行處理。
有贊在監控到熱key后,Hermes服務端集群會通過各種手段通知各業務系統里的Hermes-SDK,告訴他們:"老弟,這個key是熱key,記得做本地緩存。"
於是Hermes-SDK就會將該key緩存在本地,對於后面的請求。Hermes-SDK發現這個是一個熱key,直接從本地中拿,而不會去訪問集群。

除了這種通知方式以外。我們也可以這么做,比如你的流式計算系統監控到熱key了,往zookeeper里頭的某個節點里寫。然后你的業務系統監聽該節點,發現節點數據變化了,就代表發現熱key。最后往本地緩存里寫,也是可以的。

通知方式各種各樣,大家可以自由發揮。本文只是提供一個思路。

總結

希望通過本文,大家明白如何處理生產上遇到的熱key問題。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM