Java生鮮電商平台-緩存架構實戰
說明:在Java生鮮電商中,緩存起到了非常重要的作用,目前整個項目中才用的是redis做分布式緩存.
緩存集群
緩存集群存在的問題
1.熱key
緩存集群中的某個key瞬間被數萬甚至十萬的並發請求打爆。
2.大value
某個key對應的value可能有GB級的大小,導致查詢value的時候導致網絡相關的故障問題。
緩存集群作用
在緩存里放一些平時不怎么變動的數據,然后用戶在查詢大量的平時不怎么變動的數據的時候,可以直接從緩存里走了。緩存集群的並發能力是很強的,而且讀緩存的性能是很高的。
緩存實踐案例
- 假設系統每秒有2萬請求,但是其中90%都是讀請求,假如每秒1.8萬請求都是在讀一些不太變化的數據。那此時你把這些數據都放在數據庫里,然后每秒發送2萬請求到數據庫上讀寫數據,感受一下這樣合適?
- 如果要用數據庫承載每秒2萬請求的話,那很可能就得搞分庫分表 + 讀寫分離。
- 那得分3個主庫,承載每秒2000的寫入請求,然后每個主庫掛3個從庫,一共9個從庫承載每秒1.8萬的讀請求。
- 這樣的話,就需要一共是12台高配置的數據庫服務器,這是很耗費錢的,成本非常高,很不合適。
- 因此,可以把平時不太變化的數據放在緩存集群里,緩存集群可以采用2主2從,主節點用來寫入緩存,從節點用來讀緩存。
- 以緩存集群的性能,2個從節點完全可以用來承載每秒1.8萬的大量讀請求,然后3個數據庫主庫就是承載每秒2000的寫請求和少量其他讀請求就OK了(數據一致性問題)。
- 這樣一來,耗費的機器瞬間變成了4台緩存機器 + 3台數據庫機器 = 7台機器,是不是比之前的12台機器減少了很大的資源開銷?
- 緩存其實在系統架構里是非常重要的組成部分。很多時候,對於那些很少變化但是大量高並發讀的數據,通過緩存集群來抗高並發讀,是非常合適的。
熱點緩存
- 所謂熱點緩存問題就是突然因為莫名的原因,出現大量的用戶訪問同一條緩存數據。碰巧這些key都存在於一台緩存機器上。
- 假設每秒突然奔過來20萬請求到這台機器上,那台被20萬請求指向的緩存機器就會過度操勞而宕機的。
- 讀請求發現讀不到數據,會從數據庫里提取原始數據,然后放入剩余的其他緩存機器里去。但是接踵而來的每秒20萬請求,會再次壓垮其他的緩存機器。
- 以此類推,最終導致緩存集群全盤崩潰,引發系統整體宕機。
基於流式計算技術的緩存熱點自動發現
- 其實這里關鍵的一點,就是對於這種熱點緩存,你的系統需要能夠在熱點緩存突然發生的時候,直接發現他,然后瞬間立馬實現毫秒級的自動負載均衡。
- 一般出現緩存熱點的時候,每秒並發肯定是很高的,可能每秒都幾十萬甚至上百萬的請求量過來,這都是有可能的。
- 所以,此時完全可以基於大數據領域的流式計算技術來進行實時數據訪問次數的統計,比如storm、spark streaming、flink。
- 一旦在實時數據訪問次數統計的過程中,比如發現一秒之內,某條數據突然訪問次數超過了1000,就直接立馬把這條數據判定為是熱點數據,可以將這個發現出來的熱點數據寫入比如zookeeper中(監聽事件)。
- 流式計算系統在進行數據訪問次數統計的時候,會不會也存在說單台機器被請求每秒幾十萬次的問題呢?否!!!
- 流式計算技術,尤其是storm這種系統,他可以做到同一條數據的請求過來,先分散在很多機器里進行本地計算,最后再匯總局部計算結果到一台機器進行全局匯總。
- 所以幾十萬請求可以先分散在比如100台機器上,每台機器統計了這條數據的幾千次請求。
- 然后100條局部計算好的結果匯總到一台機器做全局計算即可,所以基於流式計算技術來進行統計是不會有熱點問題的。
熱點緩存自動加載為JVM本地緩存
- 我們自己的系統可以對zookeeper指定的熱點緩存對應的znode進行監聽,如果有變化立馬就可以感知到了。
- 此時系統層就可以立馬把相關的緩存數據從數據庫加載出來,然后直接放在自己系統內部的本地緩存里即可。
- 這個本地緩存,用ehcache、hashmap,其實都可以,一切看自己的業務需求。我們這里主要說的就是將緩存集群里的集中式緩存,直接變成每個系統自己本地實現緩存即可,每個系統本地是無法緩存過多數據的。
- 因為一般這種普通系統單實例部署機器可能就一個4核8G的機器,留給本地緩存的空間是很少的,所以用來放這種熱點數據的本地緩存是最合適的,剛剛好。
- 假設系統層集群部署了100台機器,此時你100台機器瞬間在本地都會有一份熱點緩存的副本。
- 然后接下來對熱點緩存的讀操作,直接系統本地緩存讀出來就給返回了,不用再走緩存集群了。
- 這樣的話,變成100台機器每台機器承載數千請求,那么那數千請求就直接從機器本地緩存返回數據了,這是沒有問題的。
限流熔斷保護
- 在每個系統內部,還應該專門加一個對熱點數據訪問的限流熔斷保護措施。
- 每個系統實例內部,都可以加一個熔斷保護機制,假設緩存集群最多每秒承載4萬讀請求,那么你一共有100個系統實例。
- 應該提前限制好,每個系統實例每秒最多請求緩存集群讀操作不超過400次,一超過就可以熔斷掉,不讓請求緩存集群,直接返回一個空白信息,然后用戶稍后會自行再次重新刷新頁面之類的。
-
通過系統層自己直接加限流熔斷保護措施,可以很好的保護后面的緩存集群、數據庫集群之類的不要被打死。