一、分片集群配置
分片集群的原理,只不過是將多個復制集聯合起來,每個復制集具有一個主實例和多個從實例。並且每個復制集朱保存一部分數據庫中的鍵值對,解決了主從復制集中總數據存儲量收最小實例的限制而形成木桶效應。redis的分片集群可以在數據量不斷增大的情況下進行水平擴容,將鍵值放在指定的實例中。分片集群的結構圖如下。
所有的節點都具有監控功能,在出現某個節點宕掉,其他節點會通過(PING-PONG命令)感知,並選舉出新的主節點。
1、手動配置集群
1、創建多個實例這里主要使用一下IP和端口:
192.168.121.12:6379
192.168.121.12:6380
192.168.121.12:6381
192.168.121.14:6379
192.168.121.14:6380
192.168.121.14:6381
其中192.168.121.12:6379、192.168.121.12:6380、192.168.121.14:6379為集群的主節點,192.168.121.12:6381、192.168.121.14:6380、192.168.121.14:6381為從節點。
2、cluster配置文件
cluster-enabled yes //開啟redis的集群模式
cluster-config-file nodes-6379.conf //該文件會持久化的將集群中的狀態持久化的存儲到該配置文件中
cluster-node-timeout 5000 //節點間超時互聯的閾值。
3、通過啟動指令將六個redis實例全部啟動。
4、節點點握手
在任一節點上對其他五個實例實現握手。本實踐在192.168.121.12:6379上進行握手。
192.168.121.12:6379> cluster meet 192.168.121.12 6380
192.168.121.12:6379> cluster meet 192.168.121.12 6381
192.168.121.12:6379> cluster meet 192.168.121.14 6379
192.168.121.12:6379> cluster meet 192.168.121.14 6380
192.168.121.12:6379> cluster meet 192.168.121.14 6381
握手的目的是讓各個實例之間能夠互相了解,知道其他實例的基本信息,進行信息互通。
4、插槽的分配
所有的鍵值只能分布在插槽上進行存儲,一個集群只能有16384個插槽,為了能夠實現分片集群,讓所有的鍵值分布在整個集群中,需要將16384個插槽均勻的分布到主數據庫上。實現讀寫均衡,減輕服務器壓力。每個實例都有自己的唯一標識ID,插槽需要通過數據庫ID進行分配。在這里需要兩步走,設置主節點和從節點。
設置主節點:
現在有三個主節點,需要將插槽分配給主節點,分布情況為{0----5461}、{5462-----10922}、{10923----16383}。插槽的設置主節點的插槽命令:cluster addslots {slotn..slotm}。在各個主節點上的執行步驟如下:
192.168.121.12:6379> cluster addslots {0..5461}
192.168.121.12:6380> cluster addslots {5462..10922}
192.168.121.14:6379> cluster addslots {10923..16383}
查看實例信息的指令為cluster nodes。
上圖中的前半部分為每個實例的ID,中間部分為實例的ip與port。現在需要設置主節點與從節點。
設置從節點:
通過上圖可以查到各個節點的ID,現在需要通過主節點的ID來設置從節點,命令為cluster replicate NODEID。這一步需要在從數據庫上操作。步驟如下:
192.168.121.12:6381> cluster replicate ad708e25056e7792e95cb11e67bdfdbad1f7d539
192.168.121.14:6380> cluster replicate df8c6d499e3eb6d985d200029124b49f3237e623
192.168.121.14:6381> cluster replicate 087510c60f9647b8c24e1ce9c9d9f70ad16ce1e8
查看實例信息的指令為cluster nodes
可以看到在第三部分有slave和master,這兩個標識為從節點和主節點。從節點的后面的編碼為主節點的ID,主節點的最后的數字區間為插槽的分配情況。
5、讀寫分離
開啟與 Redis Cluster 從節點連接的讀請求通常,從節點將重定向客戶端到認證過的主節點,以獲取在指定命令中所涉及的哈希槽,然而客戶端能通過READONLY
命令將從節點設置為只讀模式。
READONLY
告訴 Redis Cluster 從節點客戶端願意讀取可能過時的數據並且對寫請求不感興趣。
2、增加新節點
在添加新節點時,如果添加的是主節點則需要兩步走,即節點握手和分配插槽。如果是添加從節點,也需要兩步走,即節點握手和復制主節點的ID。
添加主節點:
按照節點配置步驟,將新節點的配置文件設置為集群模式。然后在集群的任一節點使用cluster meet ip port命令與新節點進行握手。
需要將插槽分配給新節點,此時也會分為多種情況,1、插槽未分配過。2、插槽已經分配過。
添加的節點是從節點:
首先也是進行握手操作。
其次是復制主節點的ID cluster replicate NODEID
3、分配插槽
1、插槽未分配過,如同在搭建分片集群時,為主節點分配插槽的命令是一樣的。cluster addslots slot1[slot2....slotn]
2、插槽已經分配過,需要將鍵值都要遷移過去,主要分兩步走:遷移插槽和遷移鍵值。
遷移插槽 cluster setslot slot node 新運行節點的nodeid
查詢出該插槽的鍵數 cluster countkeysinslot <slot>
返回該插槽上的鍵 cluster getkeysinslot <slot> <count>
遷移插槽上的全部鍵值 migrate 目標ip 目標port 鍵名 數據庫號碼 超時時間 [COPY] [REPLACE]
附加:在鍵的遷移過程中會遇到兩種情況:1、在鍵遷移前客戶端會請求新節點,但是此時鍵值沒有遷移,無法讀取到鍵值。2、在鍵值遷移后,客戶端可能請求舊節點,此時也會造成數據丟失。遇到這兩種情況可以使用如下步驟進行。
(1)cluster setslot <slot> importing <node_id> :從 node_id 指定的節點中導入槽 slot 到本節點。
(2)cluster setslot <slot> migrating <node_id> :將本節點的槽 slot 遷移到 node_id 指定的節點中。
(3)cluster setslot <slot> node <node_id>:將槽 slot 指派給 node_id 指定的節點,如果槽已經指派給
(4)cmigrate <ip> <port> <key> <dbnumber> timeout [COPY] [REPLACE]
(5)cluster setslot <slot> node <node_id>
運行機制:在這里將節點A認為被遷移插槽的新節點,節點B為插槽的舊節點。在客戶端在請求節點A時,如果鍵值在節點A,則直接獲取;如果鍵值不在節點A,節點A會發送一個ASK跳轉請求,告訴客戶端在節點B上。此時客戶端會發送一個ASKING命令,接着發送獲取鍵值的命令,如果節點B收到過ASKING命令,就會返回該鍵值;如果沒有收到過ASKING命令,就會返回MOVE命令,重定位到節點A。
3、常用命令
集群
cluster info :打印集群的信息
cluster nodes :列出集群當前已知的所有節點( node),以及這些節點的相關信息。
節點
cluster meet <ip> <port> :將 ip 和 port 所指定的節點添加到集群當中,讓它成為集群的一份子。
cluster forget <node_id> :從集群中移除 node_id 指定的節點。
cluster replicate <node_id> :將當前節點設置為 node_id 指定的節點的從節點。
cluster saveconfig :將節點的配置文件保存到硬盤里面。
槽(slot)
cluster addslots <slot> [slot ...] :將一個或多個槽( slot)指派( assign)給當前節點。
cluster delslots <slot> [slot ...] :移除一個或多個槽對當前節點的指派。
cluster flushslots :移除指派給當前節點的所有槽,讓當前節點變成一個沒有指派任何槽的節點。
cluster setslot <slot> node <node_id> :將槽 slot 指派給 node_id 指定的節點,如果槽已經指派給另一個節點,那么先讓另一個節點刪除該槽>,然后再進行指派。
cluster setslot <slot> migrating <node_id> :將本節點的槽 slot 遷移到 node_id 指定的節點中。
cluster setslot <slot> importing <node_id> :從 node_id 指定的節點中導入槽 slot 到本節點。
cluster setslot <slot> stable :取消對槽 slot 的導入( import)或者遷移( migrate)。
鍵
cluster keyslot <key> :計算鍵 key 應該被放置在哪個槽上。
cluster countkeysinslot <slot> :返回槽 slot 目前包含的鍵值對數量。
cluster getkeysinslot <slot> <count> :返回 count 個 slot 槽中的鍵
4、對節點和插槽操作的節本步驟總結
在添加節點時,先握手,后移動鍵值,最后移動插槽
添加從節點時,先握手,后復制主節點ID
刪除主節點時,先移動鍵值,再移動插槽,后刪除節點
刪除從節點,直接刪除即可。
5、故障恢復
1、實時監控
所有的節點可以被認為是哨兵,每個節點會每1秒隨機選取5個節點作為監控對象。並從這5個對象中選擇一個最久沒有響應的節點發送PING命令,如果節點A沒有收到節點B的響應,就會認為節點B主觀下線,同時廣播給其他節點,其他節點會記錄下節點下線信息。並且出節點A之外的其他節點也會對節點B進行監控操作,如果節點C收到半數以上節點認為節點B下線,就會標記為客觀下線。
2、選舉主節點
如果某一個從節點發現主節點已經客觀下線,就會向其他節點發送請求,要求對方選舉自己為主節點。
如果對方沒有選舉過其他節點,則統一該從節點為主節點。
只要超過半數的節點統一該從節點為主節點,那么就會成功的選舉為主節點。
如果選舉不成功,會等待一個隨機時間,重現進行選舉。
二、redis的設計原理
1、集群基本運行機制
1、寫安全
通常存在一個時間窗口,可能在分片中丟失寫入數據。 但是一個連接到絕大部分master節點的客戶端的時間窗口,與一個連接到極小部分master節點的客戶端的時間窗口 有很大的區別。
(1)主從同步時
在客戶端將數據寫入master節點時,雖然寫入master節點成功,但是在同步slave節點時,master節點可能會發生宕機事故,一旦宕機,就會丟失部分無法同步到slave節點的數據。
(2)主從切換
在master發生宕機之后,slave節點會升級為master節點,一段時間之后,master節點恢復工作。但是在恢復工作之前,每個客戶端會保存一份過期的路由表,在這段時間內客戶向
宕機的 master節點發送數據,會將寫入的數據丟。
(3)檢測到master節點宕機的time_out時間內
在當master節點在宕機之后,如果在在time_out時間內沒有發送求求進行master節點監控,而是在time_out的時間點進行監控,此時所有客戶端對該master節點的數據操作都會導致
數據丟失,只有在time_out時間段后才不會出現數據丟失。
2、可用性
在有多個master節點的集群中如果少數master節點出現數據故障,如果該部分節點有至少一個slave節點,那么在(time_out+N)秒后,該部分master節點的其中一個slave會升級為master。從
而保證集群的可用。例如:有一個集群有N個master節點,每個master節點有M個slave節點,此時如果其中一個master節點宕掉后,可用的概率為(1-1/(N*M-1))。如果某個master節點只剩一個
slave節點時,出現集群不可用的概率為(1/((N-1)*M+1))。由於在出現故障時,導致部分節點會出現孤立的master節點,redis集群會將其他master節點的slave節點副本遷移,變為孤立master節
點的從節點,從而達到集群的均衡。
3、性能
在集群中客戶端在發送請求到服務端,服務端如果負責該部分鍵,將會把鍵的值傳遞到客戶端,如果不在該服務端,會發送重定向MOVE命令,提示客戶端向指定的服務端操作數據。客戶端
也會更新自己的路由表為最新數據。所以客戶端對服務集群的操作是透明的。
4、鍵分布模型
鍵的空間會分配到16384個插槽中,所以在分配集群時,能夠將插槽分配的最大master節點數是16384個,但是建議在建立最大節點數時,以1000個為標准。鍵的分配是通過CRC-16算法計算
出所在的插槽,並將鍵值合理的存儲到插槽。分配插槽的公式為:slot = CRC16(key)%16384。
5、鍵哈希標簽
為了在一個插槽上實現多鍵操作,可以通過哈希標簽實現,如果某一個鍵名出現{}這樣的符號,那么計算哈希槽的字符串為{}內的內容。字符串以第一個出現{}為准。
例如user1000}.following 和 {user1000}.followers這樣的鍵會分配到同一個插槽中。
6、集群節點屬性
在集群中中每個節點都有自己唯一的ID作為標識,並且該表示在全局是唯一。該ID會保存到node.conf文件中,只有將該文件被刪除或者通過cluster reset命令進行重新設置才會將ID重新生成。以
下內容為節點信息:
$ redis-cli cluster nodes d1861060fe6a534d42d8a19aeb36600e18785e04 127.0.0.1:6379 myself - 0 1318428930 1 connected 0-1364 3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 2 connected 1365-2729 d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 3 connected 2730-4095
以上列表信息中每行保存的內容為:節點ID、節點IP、節點端口、節點角色、節點最后一次發送ping的時間、最后一次發送pong命令的時間、配額、連接狀態和分配的插槽。
7、集群總線與網絡拓撲
集群之間的通信時通過集群總線和集群協議進行,他們的使用端口為每個實例端口號加上10000。每個節點都會擁有一個這樣的端口作為接受來自其他節點信息的端口,而每個節點都有N-1個 對內
節點和N-1個對內節點進行信息通信。
8、節點握手
在集群中所有的節點是通過集群端口和總線進行握手的,握手是使用的meet命令進行介紹自己,這和ping消息的原理是一樣的。通過meet命令可以讓對方接受自己並成為該集群的一部分,由於在
集群內部幾點可以通過廣播的方式將該節點所了解的集群信息發送給其他節點,也就是說只要集群中的某一個節點知道有一個新節點加入集群,其他節點也會知道。所以在執行握手操作時,只需在一個
實例上執行即可。
9、MOVED重定向
在客戶端發送數據請求到節點,如果節點有該鍵所分配的插槽,則會簡單的執行該操作。如果該節點沒有該插槽,節點會通過保存在本地的插槽---節點映射表,查找該鍵所在的插槽和節點,並在回
復客戶端時附加該信息,客戶端收到該信息需要進行兩步操作:1、向指定的節點發送上次的數據請求,如果存在,則返回數據,如果不存在,通過返回的節點信息向該節點進行請求,知道獲取到數據
為止。2、為了能夠高效的執行數據操作,客戶端會記錄插槽與節點的映射關系,由於每次的MOVED返回信息中都有插槽的變更,所以客戶端會對插槽的信息進行變更。以此獲取最新的映射路由。
10、集群在線重新配置
在線重置主要是進行一下操作:1、節點的添加,2、節點的刪除,3、重新均衡。在進行以上操作的時候,都會通過goosip協議向整個集群廣播該消息,讓整個集群對自己的路由列表進行更新,保證
集群信息的一致性。以下子命令實現重新配置:
CLUSTER ADDSLOTS slot1 [slot2] … [slotN]
CLUSTER DELSLOTS slot1 [slot2] … [slotN]
CLUSTER SETSLOT slot NODE node
CLUSTER SETSLOT slot MIGRATING node
CLUSTER SETSLOT slot IMPORTING node
11、ASK 重定向
ASK與MOVED相似,都會進行請求的重定向。MOVED的重定向是將請求永久的指向新的節點,並且在獲取MOVED信息之后會更新客戶端的路由。而ASK只是臨時性的指向新的節點,該鍵可能會
在一段時間之后,回到返回ASK的節點,所以在收到ASK信息之后,無需更新路由列表。
ASK實現重定向的步驟:
首先在收到ASK信息之后,客戶端會對原有的節點發送ASKING請求。
在發送ASKING命令之后,接着發送數據操作請求。如果原有節點沒有收到ASKING請求,則不會處理數據操作,如果收到ASKING請求,則會處理該數據操作。
如果數據沒有在原有節點,則原有節點會發送MOVED響應。
12、客戶端首次連接和處理重定向
在節點啟動時,會初始化集群的路由信息
在收到MOVED信息后節點也會更新為最近的路由信息
14、通過slave節點水平擴展讀
客戶端在發送請求到slave節點時,一般情況下,slave將請求會moved到指定的master,但是我們也可以通過readonly指令指定slave為只讀模式,實現讀能力的擴展。
2、容錯性
1、節點心跳
在每秒內節點都會發送ping指令到其他某個節點,同時也會收到其他節點的pong信息。所有的節點信息都會保存到ping\pong命令中。在ping\pong命令中有兩部分:請求頭和gossip字段。
請求頭:
節點ID
configEpoch和currentEpoch
節點標識
節點負責的哈希槽
主節點的ID
集群狀態
發送端的集群端口號
gossip字段,該字段會保存集群中的部分節點的信息:
節點ID
節點IP和端口號
節點的運行狀態 //PFAIL和FAIL
2、失效檢測
在心跳檢測時發送的ping和pong命令會在gossip字段中包含該標識,該標識有兩個標識組成,PFAIL和FAIL,PFAIL表示可能失效,FAIL表示確實失效。
PFAIL:
在節點A發送ping指令到節點B后,節點A在一半的time_out時間內沒有收到來自節點B的pong回復,節點A會立刻重新發送請求到節點B,如果在從第一次發送ping命令的time_out時間
內沒有收到回復,就會將該節點在本地的路由表中將該節點標識為PFAIL。
FAIL:
由於節點心跳的原理,每個節點會收到包含其他節點的列表信息,當收到其他節點的gossip片段的節點B的信息為PFAIL的節點數超過一半,就會認為節點B為FAIL,將更新本地節點的路
由信息。將節點B標識為FAIL的節點會向集群中的其他節點發送節點B已經為FAIL,強制其他節點將節點B標識為FAIL。
3、配置處理,傳播,和失效轉移
一旦出現節點失效就要通過故障轉移實現集群的有效運行。
currentEpoch:
在節點剛剛加入的時候,currentEpoch的值為0,只有在心跳檢測的時候會接收到其他節點的epoch,如果currentepoch的值小於其他節點的epoch,則會將currentepoch的值改為最大
的epoch,最大的currentepoch值代表該節點的路由信息為最新信息。
configEpoch
該參數主要用於保存master的configepoch,slave會通過心跳檢測獲取master的configepoch,slave會實時更新configepoch為最新獲取的master的configepoch。同時其他節點收到slave
的configeoch進行判斷是否需要更新路由信息。
slave排名
slave發起投票請求的順序是通過一個排名進行的,該排名是按照slave在同步master的數據操作指令的偏移量來排序。排名越靠前的,執行發起投票請求的延遲越短,延遲時間的計算公式是:
DELAY = 500 milliseconds + 0-500隨機數 milliseconds + SLAVE_RANK * 1000 milliseconds.
發送選舉請求和slave升級
在slave發送投票之前會增加configepoch的值,並向其他的master節點發送投票請求,master節點在沒有對其他slave節點投票,就會對該節點進行投票,該slave節點收到回復的最大延遲時
間是time_out。並且其他master節點在發送投票之后的time_out*2的時間內不能對其他節點進行投票,這樣也避免了對此投票選出多個master的問題。slave節點每收到一次投票就會將自己
的configepoch進行增大,slave節點對小於自己epoch的ACK就會丟棄,防止投票重復計數。一旦一個slave升級為master,該節點的configepoch為最大值,同時向集群的全部節點發送信息,進行
路由更新。
4、master節點回復slave節點的投票請求
master在進行投票前需要驗證是否需要對slave進行投票,進行驗證的三個條件:
1、該slave的master已經標記為FAIL.
2、master的lastVoteEpoch要小於請求中的currentepoch,這樣就會保證了重復投票。
3、master的currentepoch要小於請求里的currentEpoch,這樣就會保證該請求的slave的基本信息是最新。
5、哈希槽信息的傳播
插槽信息的傳播主要有連部分組成:
心跳包:
在心跳包中會保存一份部分節點的基本信息,接收請求包的節點會與本地信息進行比對,判斷是否更新。
update信息:
如果接收到的心跳包中的configepoch小於本節點的configepoch,則會發送一個update的返回數據,對發送心跳包的節點信息進行更新。
6、節點加入並分配插槽
在有新節點加入集群,並將節點A的插槽全部分配給新節點,那么新節點將會成為節點A的master節點,節點A的slave節點也會成為新master節點的slave。但是並不是將節點A的所有插槽分配給
新節點,可能分配給其他的多個master,只有接收節點A的最后一個插槽的節點會成為節點A的master節點。
7、備份遷移和備份遷移算法
為了能夠保證集群的正常工作,並且能夠有效的利用服務器,故障轉移具有以下策略:如果節點A有一個slave節點A1,節點B有一個slave節點B1,節點C有兩個slave節點C1和C2.那么節點A或節點
B入股出現故障,slave節點A1或節點B1會升級為master節點,同時節點C的slave節點C1或C2中的一個轉換為A1或者B1的從節點,從而保障集群的可用於故障轉移。
執行此操作的配置參數是:cluster-migration-barrier,一個master節點在擁有多少個健康slave節點的時候就要割讓一個slave節點出來。例如這個參數設為 2,那么只有當一個master節點擁有 2 個可
工作的slave節點時,它的一個slave節點會嘗試遷移。
算法:
當檢測出一個master節點為孤立節點,就會觸發此算法,並找出某些master節點下應有最多slave節點並且這些slave節點均不為FAIL狀態,如果這樣的master節點有多個,可以從這些master節點
下找出各自ID最小的slave節點,如果這些slave節點均認為是ID最小的節點,則就會全部遷移到孤立master節點下成為slave節點。結果會導致其他原有的master節點下無slave節點,同時也會再次觸發
此算法據需執行,直到集群均衡為止。
8、節點重置
在將集群重新配置或者將節點加入其他集群時,需要對節點進行重置,從而成為新的未加入集群的節點,重置分為軟重置和硬重置。
命令如下:
cluster reset soft
slave節點會成為master節點,所有的數據都會丟失,插槽全部丟棄,節點列表中的其他節點全部移除
master節點如果沒有數據則和slave節點的重置相同,如果有數據就不會執行重置操作,知道數據全部刪除。
cluster reset hard
slave節點會成為master節點,所有的數據都會丟失,插槽全部丟棄,節點列表中的其他節點全部移除,重新分配節點ID,currentepoch、configepoch歸為0。
master節點如果沒有數據則和slave節點的重置相同,如果有數據就不會執行重置操作,知道數據全部刪除。
10、集群刪除節點
在將節點從集群刪除時,需要先將數據遷移到其他節點,然后執行CLUSTER FORGET <node-id> 命令來實現,該命令的會執行兩件事:
1、從指定節點的節點列表中刪除節點。
2、在60s內組織該節點同步其他節點,防止節點間的心跳檢測更新該節點的節點列表,將應刪除的節點重新添加上。