01—背景
一開始並無打算梳理redis的相關內容,由於在一篇文章中看到關於熱點問題的處理,心中有一些疑惑,內容以下:java
緩存熱點:程序員
對於特別熱的數據,若是大部分甚至全部的業務都命中同一份緩存數據,則這份數據所在緩存服務器壓力就很大,例如,某明星微博發佈「咱們」來宣告戀愛了,則短期內有成千上萬的用戶都來圍觀。面試
緩存熱點解決方案:redis
就是複製多份緩存,將請求分散到多個緩存服務器上,減輕緩存熱點致使的單臺緩存服務器壓力,以新浪微博爲例,對於粉絲超過100萬的明星,每一條微博均可以生成100分緩存,緩存的數據都是同樣的,經過緩存key裏面加編號進行分區,每次讀緩存都隨機讀取其中某份緩存。算法
解決思路是沒有問題的,是經過備份相同的數據到多臺緩存服務器中,緩解分散單臺服務器的壓力,個人疑惑是"經過緩存key裏面加編號進行分區"這種方式是怎麼確保使須要保存的數據都分散到不一樣的服務器呢?redis集羣根據key進行hash散列算法,最終映射的是槽位,不一樣的緩存服務器分別管理的是一些列表槽位,怎麼保證經過key計算出來的槽位值正好不在同一臺機器上呢?數據庫
帶着這個疑問點,開始了redis複製,哨兵,集羣內容的整理。設計模式
02—內容梳理
複製緩存
在分佈式系統中,爲了解決單點問題,一般會把數據複製多個副本部署到其餘的機器,知足故障恢復和負載均衡的需求。哨兵和集羣模式都是在複製的基礎上實現的高可用。安全
redis複製拓撲極其應用服務器
拓撲結構分爲一主一從,一主多從,樹狀多從結構。
1. 一主一從結構
應用場景:主節點出現宕機時,從節點提供故障轉移支持。
當寫命令併發量較高而且須要持久化時,能夠在從節點開啓AOF,這樣既保證了數據的安全也避免了持久化對於主節點的影響。
注意:
當主節點關閉持久化功能,在從節點實現的時候,若是主節點脫機要避免主節點自動重啓,由於主節點沒有開啓持久化功能,自動重啓後數據集爲空,這時若是從節點繼續複製主節點會致使從節點數據也被清空。
解決方案:
在從節點執行slaveof no one斷開與主節點的複製關係,再重啓主節點避免這種狀況。
2. 一主多從結構
利用多個節點實現讀寫分離,適用於讀佔比大的場景,把讀命令發送到從節點來分擔主節點壓力。
平常開發中,執行一些比較耗時的讀命令,能夠在其中的一個從節點上進行,防止慢查詢對主節點形成阻塞
3. 樹狀主從結構
從節點不但能夠複製主節點的數據,同時能夠做爲其餘從節點的主節點繼續向下層複製。
有效的下降主節點的負載和須要傳送給從節點的數據量,下降主節點壓力。
複製模式的注意事項:
1. 複製的數據流是單向的,只能從主節點複製到從節點。
2. 從節點默認使用slave-read-only=yes配置爲只讀模式,對於從節點的任何修改主節點是無感知的,修改從節點會形成主從數據不一致,因此建議不要修改從節點的只讀模式。
3. 讀寫分離面臨的問題
對讀佔比高的場景,把一部分讀流量分攤到從節點來減輕主節點壓力,永遠只對主節點執行寫操做。
當使用從節點影響請求時,業務端可能面臨的問題:
複製數據延遲
剛在主節點寫入數據後馬上在從節點讀取可能讀取不到,須要業務場景容許短期內數據的延遲。
當沒法容忍延遲場景,能夠編寫外部監控程序,監聽主從節點的複製偏移量,當延遲較大時觸發報警或者通知客戶端避免讀取延遲太高的從節點。
監控程序按期檢查主從節點偏移量,當延遲字節太高時,例如超過10MB,監控程序觸發報警通知客戶端從節點延遲太高,客戶端接收到從節點太高延遲通知後,修改讀命令路由到其餘的從節點或者主節點上,當延遲恢復後,再次通知客戶端,恢復從節點的讀命令請求。
讀到過時數據
Redis內部維護過時數據刪除策略,刪除策略一共有兩種,惰性刪除和按期刪除。
惰性刪除:主節點每次處理讀請求命令時,都會檢查鍵是否超時,若是超時,則執行del命令刪除鍵對象,以後del命令也會異步發送給從節點,爲了保證數據一致性,從節點自己永遠不會主動刪除超時數據。
若是在從節點讀取數據,則獲取到的數據就有多是已通過期的數據了,由於此時讀請求沒有發送到主節點,主節點不會檢查鍵是否過時也不會發送del命令給從節點。
按期刪除:Redis主節點內部定時任務會循環採樣必定數量的鍵,當發現採樣的鍵過時時執行del命令,以後同步給從節點。
若是此時數據大量超時,主節點採樣的速度跟不上過時的速度且主節點沒有讀取過時鍵操做,那麼從節點將沒法收到del命令,這時從節點可能讀取到已經超時的數據。
定時刪除和惰性刪除命令都會有讀取到過時數據的問題,在Rdis3.2版本以後,從節點讀取數據以前會檢查鍵的過時時間來決定是否返回數據,規避了讀取超時數據的問題。
從節點故障
對於從節點故障,須要在客戶端維護可用從節點列表,當從節點故障時馬上切換到其餘從節點或主節點上,相似於延遲太高的監控處理。
哨兵
redis主從複製模式下,一旦主節點因爲故障不能提供服務,須要人工將從節點晉升爲主節點,同時還要通知應用方更新主節點地址,爲了解決人工處理問題,Redis Sentinel(哨兵)架構解決了這個問題。
Sentinel是Redis的高可用的解決方案,由一個或多個Sentinel實例組成Sentinel系統能夠監視任意多個主服務器以及這些主服務器下的全部從服務器,而且在被監視的主服務器進入下線狀態時,自動將下線主服務器下的某個從服務器升級爲新的主服務器,而後由新的主服務器代替已下線的主服務器繼續處理命令請求。
當主節點出現故障時,Redis Sentinel能自動完成故障發現和故障轉移,並通知應用方,從而實現真正的高可用。
高可用讀寫分離思路:
從節點的做用:
第一,當主節點出現故障時,做爲主節點的後備"頂"上來,實現故障轉移,Redis Sentinel實現了該功能的自動化,實現了真正的高可用。第二,擴展主節點的讀能力,尤爲是在讀多寫少的場景,很是適用。
上述的模型中,從節點不是高可用的,若是slave-1節點出現故障,首先客戶端client-1將與其失聯,其次Sentinel節點只會對該節點作主觀下線,由於Redis Sentinel的故障轉移時針對主節點的。
不少時候Redis Sentinel中的從節點僅僅是做爲主節點的一個熱備,不讓它參與客戶端的讀操做,就是爲了保證總體的高可用,但實際上這種使用方法仍是有一些浪費,尤爲是在有不少從節點或者確實須要讀寫分離的場景,因此如何實現從節點的高可用是很是有必要的。
設計Redis Sentinel從節點的高可用,只要可以掌握全部的從節點的狀態,把全部的從節點看作是一個資源池,不管是上線仍是下線一個從節點,客戶端都能及時感知到(將其從資源池中添加或者刪除),這樣就實現了從節點的高可用
集羣
Redis cluster是Redis的分佈式解決方案,當遇到單機內存,併發,流量等瓶頸時,能夠採用Cluster架構方案達到負載均衡的目的。
數據分佈理論
分佈式數據庫首要解決的是把整個數據集按照分區規則映射到多個節點的問題。把數據集劃分到多個節點,每一個節點負責整個數據的一個子集。
數據分區規則有兩種:哈希分區和順序分區兩種。
哈希分區:離散度好,數據分佈業務無關,沒法順序訪問。
順序分區:離散度易傾斜,數據分佈業務相關,可順序訪問。
Redis Cluster使用的是哈希分區規則,哈希分區規則有如下幾種:
節點取餘分區
使用特定的數據,如Redis的鍵或用戶ID,再根據節點數量N使用公式:
hash(key) % N 計算出哈希值,用來決定數據映射到哪個節點。
問題:
當節點數量發送變化時,如擴容或收縮節點,數據節點映射關係須要從新計算,會致使數據的從新遷移。
使用場景:
經常使用於數據庫的分庫分表規則,通常採用預分區的方式,提早根據數據量規劃好分區數,好比劃分512或1024張表,保證支撐將來一段時間的數據量,再根據負載狀況將表遷移到其餘的數據庫中。
擴容時一般採用翻倍擴容,避免數據映射所有打亂致使全量遷移的狀況。
一致性哈希分區
優勢:相比於節點取餘的好處在於加入和刪除節點隻影響哈希環中相鄰的節點,對其餘的節點沒有影響。
問題:
加減節點會形成哈希環中部分數據沒法命中,須要手動處理或忽略這部分數據,因此一致性哈希經常使用於緩存場景。
當使用少許節點時,節點變化將大範圍影響哈希環中數據映射。一致性哈希不適合少許數據節點的分佈式方案。
普通的一致性哈希分區在增減節點時,須要增長一倍或者減小一半的節點纔可以保證數據和負載的均衡。
虛擬槽分區
虛擬槽分區巧妙的使用了哈希空間,使用分散度良好的哈希函數把全部的數據映射到一個固定範圍的整數集合中,整數定義爲槽(slot),整個範圍通常遠遠大於節點數量,Redis Cluster槽範圍是0-163843,槽是集羣內數據管理和遷移的基本單位,採用大範圍槽的主要目的是爲了方便數據拆分和集羣擴展。
每個節點會負責必定數量的槽。
Redis Cluster使用的虛擬槽分區,全部的鍵根據哈希函數映射到0-16383整數槽內,計算公式:slot=CRC16(key) & 16383,每個節點負責維護一部分槽以及槽所映射的鍵值數據。
Redis虛擬槽分區的特色:
解耦數據和節點之間的關係,簡化了節點擴容和縮容的難度。
節點自身維護槽的映射關係,不須要客戶端或代理服務器維護槽分區元數據。
支持節點,槽,鍵之間的映射查詢,用於數據路由,在線伸縮等場景。
集羣功能的限制:
key批量操做支持有限,如mset,mget目前只支持具備相同slot值的key執行批量操做(相同的槽位slot可能會存放多個key,根據公式可知:slot=CRC16(key) & 16383)。
key事務操做支持有限,只支持多key在同一個節點上的事務操做。
key做爲數據分區的最小粒度,不能將一個大的鍵值對象如hash,list等映射到不一樣的節點。
單機下Redis能夠支持16個數據庫,集羣模式下只能使用一個數據庫空間,即db0。
複製結構只支持一層,從節點只能複製主節點,不支持嵌套樹狀複製結構。
集羣伸縮原理:
在不影響集羣對外提供服務的狀況下,能夠爲集羣添加節點進行擴容,也能夠下線部分節點進行縮容
Redis集羣對節點靈活上下線控制,其原理可抽象爲槽和對應的數據在不一樣節點之間靈活的移動。
三個節點分別維護本身負責的槽和對應的數據,若是但願加入一個節點實現集羣擴容時,須要經過相關命令把一部分槽和數據遷移到新節點。
圖中每個節點把槽和數據遷移到新節點6385上,每一個節點負責的槽和數據相比以前變少了,從而達到了集羣擴容的目的。
集羣水平伸縮的上層原理:集羣伸縮=槽和數據在節點之間的移動。
03—解決方案
再回到開始本身的疑問,我理解我本身的想法是沒有錯的,不可以徹底保證能夠經過key來進行分區,由於生成的key經過hash算法最終獲取到的是一個槽位,並不能確保新生成的槽位分配在不一樣的機器上,通常狀況下每個節點對應的是多個槽位。
在已知熱點key的狀況下推薦使用內存存放熱key來解決。
針對這種熱key請求,會直接從jvm中獲取,避免走redis層,假設此時有10萬請求針對同一個key請求過來,若是沒有本地緩存,這十萬個請求直接到了redis,會將redis服務器弄掛,若是應用服務器有50臺,同時熱數據在jvm中已經存儲了,這十萬請求能夠平均分散開來,每一個機器2000請求,直接從jvm中取值,而後返回數據
推薦閱讀
爲何阿里巴巴的程序員成長速度這麼快,看完他們的內部資料我懂了
看完三件事❤️
若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
點贊,轉發,有大家的 『點贊和評論』,纔是我創造的動力。
關注公衆號 『 Java鬥帝 』,不按期分享原創知識。
同時能夠期待後續文章ing🚀