Redis集羣管理

1.簡介

 

Redis在生產環境中通常是經過集羣的方式進行運行,Redis集羣包括主從複製集羣和數據分片集羣兩種類型。node

*主從複製集羣提供高可用性,而數據分片集羣提供負載均衡。redis

*數據分片集羣中能實現主從複製集羣的功能。 數據庫

 

 

2.Redis主從複製集羣

 

 

 

主從複製集羣中由Master節點提供讀寫服務,Slave節點負責同步Master節點中的數據,當Master節點發生故障時,由Slave節點充當Master對外提供服務。ruby

主從複製集羣中可使用一主一從模式,也可使用一主多從模式,在一主多從模式中主節點須要將修改同步給各個從節點從而增長了主節點的壓力(帶寬)網絡

*在主從複製集羣中Slave節點可以進行讀取(不建議),但若是沒有開啓TCP的NO_DELAY功能,那麼讀取的數據多是髒數據,在Slave節點進行寫入時,會提示Slave節點不能進行寫入。 架構

 

關於讀寫分離

應用對於數據庫而言都是讀多寫少的,即數據庫的讀取壓力要比寫入的壓力大(即100個請求95個都是讀的),因爲受數據庫自身性能影響,所以通常都會搭建主從數據庫,由多個從數據庫提供讀取服務,分擔壓力,實現讀寫分離。負載均衡

讀寫分離是相對於有磁盤IO操做的數據庫而言的,對於基於內存的NoSQL來講不存在此問題,其讀取和寫入的性能都很快,每秒能處理幾萬個請求,所以沒有必要進行讀寫分離,Redis中應用主從複製集羣是爲了保障集羣的高可用性,當Master節點發生故障時,由Slave節點充當Master對外提供服務。tcp

主從數據庫實現讀寫分離的根本緣由是數據庫自身性能低下。分佈式

 

2.1 搭建主從複製集羣

直接修改各個節點的redis.conf配置文件函數

#Slave節點同步Master節點中的數據
slaveof <master> <port>

#Slave節點修改成只讀模式
slave-read-only=yes

*當Master節點啓動後再啓動全部的Slave節點。

*當主從複製集羣搭建後能夠經過info replication命令查看集羣間的信息。

 

2.2 主從複製集羣中的數據同步

主從複製集羣中的數據同步使用PSYNC命令進行完成,PSYNC命令包含全量複製以及部分複製。

1.全量複製:首次加入集羣的Slave節點會同步Master節點中的全部數據。

2.部分複製:再次與Master節點創建鏈接的Slave節點同步Master節點中的部分數據。

*Redis提供了repl-disable-tcp-nodelay命令,表示是否啓用TCP的NO_DELAY功能,當該命令爲yes時,表示禁用TCP的NO_DELAY功能,那麼Master節點在同步修改給各個Slave節點時會合並小的TCP包從而節省帶寬,但此方式會增長同步延時(40ms左右),當該命令爲no時,表示啓用TCP的NO_DELAY功能,那麼Master節點可以實時的同步修改給各個Slave節點。

 

2.3 主從複製集羣中的故障轉移

當Master節點發生故障時,能夠經過手動或者自動的方式進行故障轉移

 

手動故障轉移

1.將其中一個存活的Slave節點斷開與Master節點的鏈接,並使其成爲新的Master節點(slaveof no one)

2.將其餘的Slave節點與該新的Master節點創建鏈接(slaveof ip port)

3.修改各個節點的redis.conf配置文件,更新主從映射關係,保證下次重啓時使用最新的主從關係啓動,避免主從數據不一致問題。

 

自動故障轉移(哨兵機制)

Redis中提供了Sentinel哨兵機制,由多個哨兵組成一個哨兵集羣,負責保障Redis集羣的高可用性,當Master節點發生故障時,自動的將其中一個Slave節點斷開與Master節點的鏈接並使其成爲新的Master節點,並將其餘的Slave節點與新的Master節點創建鏈接,最後修改各個節點的redis.conf配置文件,更新主從映射關係。

 

1.每一個哨兵節點每隔10s會向Master發送info replication命令,獲取當前集羣最新的拓撲結構,此時每一個哨兵就能獲取到各個Slave節點的鏈接信息。

2.每一個哨兵每隔1s會向集羣中的Master和各個Slave節點發送心跳,根據心跳來判斷節點是否存活,若在必定時間內節點沒有回覆,那麼該哨兵認爲該節點已經故障。

3.每一個哨兵每隔2s會向Redis中的指定頻道發佈其對Master節點的判斷,同時每一個哨兵會訂閱該頻道,所以每一個哨兵都能知道其餘哨兵對Master節點的判斷。

4.當其中一個哨兵發現Master節點故障後,會查看其餘哨兵對Master節點的判斷,若超過指定個數個哨兵都認爲該節點故障,那麼由該哨兵充當哨兵集羣的Leader進行故障轉移,故障轉移的步驟與手動轉移的一致,挑選其中一個存活的Slave節點斷開與Master節點的鏈接,並使其成爲新的Master節點,而後將其餘Slave節點與該新的Master節點創建聯繫,最後修改各個節點的redis.conf配置文件,更新主從映射關係,保證當集羣重啓時以最新的主從映射關係運行,避免發生主從數據不一致的問題。

 

在Redis的源碼目錄中存在sentinal.conf配置文件,該文件是哨兵的配置文件。

#監聽Master節點的信息
sentinel monitor <master-name> <ip> <port> <quorum>

*其中quorum表示當哨兵集羣中有quorum個哨兵都認爲Master節點不可用時則哨兵集羣認爲該節點已經故障.

#心跳超時時間
sentinel down-after-milliseconds <master-name> <milliseconds>

#故障轉移超時時間
sentinel failover-timeout <master-name> <milliseconds>

#容許同時有多少個從節點同步新節點的數據
sentinel parallel-syncs <master-name> <numreplicas>

*一個哨兵集羣能夠同時監控多個Redis主從複製集羣。

 

分別啓動各個Redis節點,而後經過redis-sentinel分別啓動各個哨兵,因爲每一個哨兵都關聯同一個Master,所以這多個哨兵自動成爲集羣關係。

*在主從複製集羣中,通常都會使用自動故障轉移方案(哨兵機制)  

 

 

3.Redis數據分片集羣

 

Redis在3.0版本後推出了RedisCluster用於搭建數據分片集羣。

 

 

*其中每一個Master節點負責指定範圍的槽以及槽範圍內的數據,並提供讀寫服務,Slave節點只負責同步Master節點中的數據,不支持進行讀取。

*使用RedisCluster時,Master節點的個數至少須要三個,每一個Master能夠有任意個Slave節點。

 

 

*RedisCluster使用虛擬槽的方式進行數據分片,Redis中虛擬槽的範圍爲0~16383(共16384個槽),每一個Master節點負責指定範圍的槽以及槽範圍內的數據(每一個槽與不少Key進行關聯,這些Key都在該Master節點的內存中)

*全部Key在進行讀取和寫入操做時,都須要根據H(K) = CRC16[K] & 16383散列函數計算出Key所坐落的槽,而後找到其對應處理的Master節點,最後自動跳轉到該節點進行操做。

 

因爲使用了RedisCluster,數據將分散到各個節點中,所以有些操做是不容許的

1.涉及多個Key的操做,好比mset、sinter等。

2.事務不能跨節點。

3.不支持多數據庫,每一個Master節點只能有一個數據庫。

4.不支持Pipeline(管道) 

 

關於數據分片的路由策略

數據分片的路由策略通常有三種,分別是除留餘數法、一致性Hash、虛擬槽,RedisCluster使用虛擬槽的方式實現數據分片。 

 

除留餘數法:以元素被某個整數M整除後所獲得的餘數找對其對應處理的節點( H(K) = K % M,M等於節點的個數)

*當增長或減小節點時,數據的路由將發生變化,伸縮性不好。

 

一致性Hash:以元素經過某個散列函數H(K)所獲得的散列值坐落在Hash環上的位置,找到其對應處理的節點。

 

1.首先將集羣中的節點IP經過散列函數H(K)計算出散列值並使其坐落在Hash環上,每一個節點負責Hash環上特定範圍的請求。

2.將元素經過相同的散列函數H(K)計算出散列值,以該散列值坐落在Hash環上的位置找對其對應的處理節點。

*使用此方式很難保證客戶端的請求平均分配到各個節點中,不能很好的實現負載均衡。

 

虛擬槽:以元素經過某個散列函數H(K)所獲得的槽位,找到其對應處理的節點。

1.每一個節點負責指定槽範圍內的請求。

2.將元素經過散列函數H(K)計算出槽位,找到其對應處理的節點。

 

 

3.1 搭建數據分片集羣 

能夠經過手動或者自動的方式搭建數據分片集羣。

 

手動搭建數據分片集羣

1.準備配置文件

#開啓RedisCluster模式
cluster-enabled yes

#RedisCluster集羣配置文件,存放集羣間節點的信息。
cluster-config-file nodes-6379.conf

#節點超時時間(ms)
cluster-node-timeout 15000

 

2.分別啓動各個Redis節點

*當啓動Redis節點後,會生成nodes.conf文件,該文件記錄着集羣間節點的關係(此時只有本節點信息)

*每一個節點都有一個ClusterID,且角色默認都是Master。 

 

3.握手(使各個節點創建關係)

*鏈接任意一個節點,而後分別對剩餘的節點進行握手。

*當握手成功後,在node.conf文件中能看到集羣間完整的節點信息。

 

4.分配槽

 

5.主從映射

*分別鏈接要做爲Slave的節點,而後經過ClusterID與Master進行關聯。

 

6.使用集羣的模式鏈接RedisCluster

*其中cluster nodes命令可以查看集羣間節點的信息,其讀取的是node.conf文件中信息,cluster info命令可以查看集羣的狀態信息。

*當槽分配完成後,此時集羣將處於上線狀態,當集羣中任意一個Master節點故障後,若是沒有對應的Slave節點,那麼集羣將處於下線狀態,當集羣處於下線狀態時,不能對外提供服務。

*當集羣搭建完成後,能夠進行關閉以及重啓,當重啓集羣時,會自動讀取node.conf文件中的信息恢復集羣間的關係,並讀取dump.rdb文件進行數據的恢復。

*當須要從新構建集羣關係時,須要刪除每一個節點的node.conf以及rdb文件,不然集羣搭建不成功。

 

*當使用集羣的模式鏈接RedisCluster後,當進行讀取和寫入操做時,會經過H(K)散列函數計算出Key所在的槽,而後找到其對應處理的Master節點,最後自動跳轉到該節點進行操做。 

*不論是讀取仍是寫入操做,都會統一跳轉到對應處理的Master節點,slave-read-only=yes配置只適用於主從複製集羣模式。 

 

自動搭建數據分片集羣

RedisCluster使用ruby來自動搭建數據分片集羣。

1.環境準備

須要安裝ruby,而且安裝redis.gem

 

2.準備配置文件

#開啓RedisCluster模式
cluster-enabled yes

#RedisCluster集羣配置文件,存放集羣間節點的信息。
cluster-config-file nodes-6379.conf

#節點超時時間(ms)
cluster-node-timeout 15000

 

3.分別啓動各個Redis節點

 

4.使用redis-trib.rb命令自動完成握手、分配槽、主從映射

redis-trib.rb create --replicas <slaveNum> <ip:port..>

*其中slaveNum爲每一個Master節點的Slave個數,能夠爲0。

*只能使用ip地址,不能使用主機名。

 

 

 

3.2 數據分片集羣中的狀態同步

RedisCluster使用基於Gossip協議的PING/PONG通信來保證集羣間狀態的同步

 

Gossip協議

Gossip協議主要用在分佈式系統中各個節點的數據同步。

Gossip協議由種子節點發起請求(種子節點即狀態發生改變的節點),當一個種子節點有狀態須要更新到網絡中的其餘節點時,它會隨機選擇周圍幾個節點進行散播消息,收到消息的節點也會重複此過程,直至網絡中的全部節點都收到消息,這個過程須要必定的時間,所以Gossip是一個最終一致性協議。
 

 

 

Gossip協議中提供了三種通信類型:

1.PUSH類型:A節點將數據發送給B節點,B節點更新A節點比本身新的數據。

2.PULL類型:A節點將數據發送給B節點,B節點返回比A節點新的數據,A節點再更新本身。

3.PULL/PUSH類型:A節點將數據發送給B節點,B節點返回比A節點新的數據,A節點再更新本身,而後A節點將數據發送給B節點,B節點更新A節點比本身新的數據。

*PUSH類型目的是讓其餘節點更新。

*PULL類型目的是更新自身節點的信息。

*每一個消息都有一個時間戳,用來區分新老信息。

 

RedisCluster中的PING/PONG通信

PING:發送集羣中節點的信息、角色、集羣ID、時間戳。

PONG:響應PING的請求。

*PING請求即Gossip協議中的PUSH,目的是讓其餘節點進行更新。

 

 

 

RedisCluster中的每一個節點都會按期的向其餘節點發送PING請求,用於集羣間狀態的同步以及檢測節點的可用性。

當集羣中有新節點加入時(通過Meet操做),該節點會向其餘節點發送PING請求,同時其餘節點也會向其發送PING請求,最終達到數據一致性。

*RedisCluster中的節點故障是經過Master投票決定的,當有半數的Master認爲該節點故障時,那麼集羣認爲該節點故障,若是故障的節點是Master,那麼會將其Slave節點切換爲Master。

*當RedisCluster中有一半的Master同時失效,那麼整個集羣將不可用,由於已經沒有足夠的Master進行投票。

 

 

4.JAVA中使用Redis集羣

 

4.1 使用主從複製集羣

*Jedis中經過JedisSentinelPool實例來使用主從複製集羣,鏈接主從複製集羣中全部哨兵的地址,並指定哨兵配置文件中Master的名稱。

/** * @Auther: ZHUANGHAOTANG * @Date: 2019/4/2 17:11 * @Description: */
public class RedisUtils { private static final String masterName = "mymaster"; private static JedisSentinelPool jedisSentinelPool = null; static { //鏈接主從複製集羣中全部哨兵地址
        Set<String> connectionMes = new HashSet<>(); connectionMes.add("192.168.2.90:26379"); connectionMes.add("192.168.2.91:26379"); connectionMes.add("192.168.2.92:26379"); //鏈接池配置
        JedisPoolConfig poolConfig = new JedisPoolConfig(); //最大鏈接數
        poolConfig.setMaxTotal(10); //最大空閒鏈接數
        poolConfig.setMaxIdle(5); jedisSentinelPool = new JedisSentinelPool(masterName, connectionMes, poolConfig); } public static Jedis getConnection() { return jedisSentinelPool.getResource(); } }

 

4.2 使用數據分片集羣 

*Jedis中經過JedisCluster實例使用數據分片集羣,鏈接數據分片集羣中全部節點(能夠直接建立JedisCluster實例,也能夠添加鏈接池進行管理)

/** * @Auther: ZHUANGHAOTANG * @Date: 2019/4/2 17:11 * @Description: */
public class RedisUtils { private static JedisCluster jedisCluster = null; static { //鏈接RedisCluster中的全部節點
        Set<HostAndPort> connectionMes = new HashSet<>(); connectionMes.add(new HostAndPort("192.168.2.90", 6379)); connectionMes.add(new HostAndPort("192.168.2.90", 6380)); connectionMes.add(new HostAndPort("192.168.2.91", 6379)); connectionMes.add(new HostAndPort("192.168.2.91", 6380)); connectionMes.add(new HostAndPort("192.168.2.92", 6379)); connectionMes.add(new HostAndPort("192.168.2.92", 6380)); //鏈接池配置
        JedisPoolConfig poolConfig = new JedisPoolConfig(); //最大鏈接數
        poolConfig.setMaxTotal(10); //最大空閒鏈接數
        poolConfig.setMaxIdle(5); jedisCluster = new JedisCluster(connectionMes, poolConfig); } }

*當使用集羣的模式鏈接RedisCluster時,當進行讀取和寫入操做時,會隨機鏈接集羣中的一個節點,而後根據H(K)散列函數計算出Key所坐落的槽,而後找到該槽所對應處理的Master節點,最後自動跳轉到該節點中進行操做。

 

 

5.Redis集羣數據遷移方案

 

5.1 主從複製集羣的數據遷移

 

 

 

當須要將當前主從複製集羣中的數據遷移到一個新的主從複製集羣時

1.搭建新的主從複製集羣,而後關閉集羣,先關閉全部Slave節點,再關閉Master節點,避免發生主從切換。

2.將舊集羣中的Master節點的RDB文件複製到新集羣中的Master節點。

3.重啓新集羣,自動進行數據的恢復。

*先關閉新集羣再進行RDB文件的遷移,是由於當節點進行shutdown操做時,會自動觸發bgsave命令,避免RDB文件被替換。

*關閉集羣時先關閉全部Slave節點再關閉Master節點,是爲了不發生主從切換,當重啓集羣時,致使主從數據不一致的問題。 

 

5.2 數據分片集羣的數據遷移

 

 

 

 

當須要將當前數據分片集羣中的數據遷移到一個新的數據分片集羣時 

1.使用與舊集羣相同的Master節點個數以及相同的槽範圍搭建新的數據分片集羣(手動/自動搭建)

2.依次關閉新集羣中的各個節點,先關閉全部Slave節點,再關閉全部Master節點,避免發生主從切換以及RDB文件被替換。

3.根據舊集羣的node.conf文件查看Master節點的相關信息,再查看新集羣的node.conf文件中Master節點的相關信息,而後將舊集羣中的Master節點的RDB文件複製到新集羣對應槽範圍的Master節點中,最後重啓新集羣,自動進行數據恢復。

*根據兩個集羣的node.conf文件來肯定相同槽範圍的Master節點的映射關係。

*不論是主從複製集羣仍是數據分片集羣,新集羣中的Slave節點的個數都不須要和舊集羣的一致。 

 

 

6.Redis集羣擴容

 

Redis集羣擴容是相對於數據分片集羣來講的,在現有集羣的架構下,經過增長多個Master節點來提升Redis集羣的負載能力。

 

 

6.1 準備要加入到集羣的節點

*根據實際的業務狀況決定要增長的Master節點個數,以及對應Slave節點的個數。

 

6.2 分別進行握手,使其加入到集羣中

 

6.3 進行槽位和數據的遷移

 

*使用redis-trib.rb reshared --timeout <timeout> <ip>:<port>命令進行槽位和數據的遷移,其中timeout指定每一個槽遷移時最大的超時時間,ip和端口指定接收槽和數據的目標節點。

*須要指定在當前集羣中移動多少個槽位到目標節點中,值能夠選1~16384,而且須要指定目標節點在集羣中的ID。

*能夠選擇all和done兩種遷移模式,其中all表示將集羣中的全部節點做爲槽的源節點(平均分配),done表示指定集羣中的某個節點做爲槽的源節點。

*在槽和數據遷移的過程當中,用戶的請求會有相應的延時,整個遷移過程所須要的時間與Redis集羣中的數據量成正比(線上試過平均2.5s遷移一個槽,遷移6000個槽耗費了4小時)

*在槽和數據遷移的過程當中,若是某個槽位在遷移時報錯,那麼Redis會中止遷移,在它以前遷移的槽和數據仍會生效,當再次遷移時須要執行fix命令(會有提示),刪除遷移失敗的槽位的數據。

*使用了all模式,可見遷移後的槽從以前的Master中分別平均抽取1364/1365個槽。

 

6.4 主從映射

相關文章
相關標籤/搜索