7、redis主從複製和sentinel配置高可用
一:redis主從配置
-
1、環境準備
master : 192.168.50.10 6179 slave1: 192.168.50.10 6279 slave2: 192.168.50.10 6379
-
2、redis.conf配置檔案配置
-
master
port 6179 requirepass 123456 #密碼認證,可以不設定 dir "/var/redis/6179" #工作目錄,dump.rdb會保留在這個目錄
-
slave1
port 6279 slaveof 192.168.50.10 6179 #master的ip和埠,不能使用127.0.0.1 6179 masterauth 123456 #如果master設定了requirepass,則這裡需要配置 slave-read-only yes #預設就是yes dir "/var/redis/6279"
-
slave2
port 6379 slaveof 192.168.50.10 6179 #master的ip和埠,不能使用127.0.0.1 6179 masterauth 123456 #如果master設定了requirepass,則這裡需要配置 slave-read-only yes #預設就是yes dir "/var/redis/6379"
注意:如果slaveof 127.0.0.1 6179,則連結sentinel時提示是127.0.0.1,無法連線
-
-
3、啟動redis並測試 ==> 如果設定了daemonize yes則沒有日誌輸出,預設是no
-
啟動: nohup redis-server /redis/redis.conf $1>>/var/log/redis/redis.log 2>&1 &
master 127.0.0.1:6179>info replication
slave1 127.0.0.1:6279>info replication
slave2 127.0.0.1:6379>info replication
-
在master中執行如下語句,如果所有機器執行keys* 都能查出name這個key說明能夠複製
127.0.0.1:6179> set name nametest
-
-
4、日誌分析
-
slave1和slave2一啟動就會去連結master
-
master啟動後,接受slave1和slave2的資訊同步請求,進行RDB持久化(在記憶體中持久化)
-
-
5、redis主從同步規則
Salve會發送sync命令到Master Master啟動一個後臺程序,將Redis中的資料快照儲存到檔案中 啟動後臺程序的同時,Master會將儲存資料快照期間接收到的寫命令快取起來 Master完成寫檔案操作後,將該檔案傳送給Salve Salve將檔案儲存到磁碟上,然後載入檔案到記憶體恢復資料快照到Salve的Redis上 當Salve完成資料快照的恢復後,Master將這期間收集的寫命令傳送給Salve端 後續Master收集到的寫命令都會通過之前建立的連線,增量傳送給salve端
總結一下,主從剛剛連線的時候,進行全量同步;全同步結束後,進行增量同步。當然,如果有需要,slave 在任何時候都可以發起全量同步
注意:在redis2.8開始,slave重新啟動,會發送psync到master進行部分同步,而不是sync全量同步
二:Sentinel 元件實現HA配置
-
1、介紹
-
Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案,功能類似於zk,用於監控redis master狀態,進行切換。
-
sentinel是和redis獨立的元件,不需要每個redis節點都啟動sentinel,可以在任何機器上部署,只要能夠連線redis master即可。
-
其功能類似於zk,用於監聽和選舉,可以啟動多個sentinel作為叢集,只要連線的master相同,則認為是統一叢集,sentinel叢集中各個sentinel也有互相通訊,通過gossip協議
-
一個sentinel可以監聽多個redis叢集,他們是通過連線到redis master,利用釋出/訂閱功能來發現其他sentinel的存在。
-
-
2、配置引數介紹
-
叢集1 mymaster
# 至少2個sentinel認為mymaster掛了,才真正認為是掛了,所以sentinel至少要有2個在執行 sentinel monitor mymaster 192.168.50.10 6179 2 # sentinel向master傳送ping,如果在這時間內沒有響應,則這個sentinel認為master掛了 sentinel down-after-milliseconds mymaster 60000 # sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1
-
叢集2 resque
sentinel monitor resque 192.168.50.10 6179 4 sentinel down-after-milliseconds resque 10000 sentinel failover-timeout resque 180000 sentinel parallel-syncs resque 5
-
-
3、工作原理
-
連結master,獲取slave和其他sentinel資訊,記錄到自己的sentinel.conf中,如下圖
-
sentinel發現down-after-milliseconds 時間內master沒有回覆,則認為failor,它會問叢集其他sentinel是否也有人認為失敗,個數達到sentinel monitor mymaster xxx 2,才會觸發failover
-
sentinel叢集選舉其中一個sentinel拿著一個唯一的版本號去進行master切換。此sentinel指定一個slave傳送SLAVE OF NO ONE,將其轉化為master,同時將這些配置資訊廣播給其他sentinel,進行更新master資訊
-
-
4、啟動 ===> 如果設定了daemonize yes則沒有日誌輸出
nohup redis-server redis-slave1/sentinel.conf --sentinel $1>>/var/log/sentinel/sentinel.log 2>&1 &
-
5、失敗測試=> 6379變成了6179的redis
-
6、注意
當一個master配置為需要密碼才能連線時,客戶端和slave在連線時都需要提供密碼。 master通過requirepass設定自身的密碼,不提供密碼無法連線到這個master。 slave通過masterauth來設定訪問master時的密碼。 但是當使用了sentinel時,由於一個master可能會變成一個slave,一個slave也可能會變成master,所以需要同時設定上述兩個配置項。
三:動態切換master測試
-
1、pom依賴
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
-
2、程式碼
public class JedisSentinelTest { public static void main(String[] args) { /** * 獲取master連結~方法一 */ HashSet<String> sentinelSet = new HashSet<>(); sentinelSet.add("192.168.50.10:26179"); // 這個節點目前是掛掉的,也可以配置,後續啟動有效 sentinelSet.add("192.168.50.10:26279"); sentinelSet.add("192.168.50.10:26379"); JedisSentinelPool myMasterSentinel = new JedisSentinelPool("mymaster", sentinelSet); Jedis master = myMasterSentinel.getResource(); master.set("aaaa", "aaaTest"); master.close(); /** * 獲取master連結~方法二 */ Jedis sentinelNode = new Jedis("192.168.50.10", 26179); List<Map<String, String>> masterList = sentinelNode.sentinelMasters(); Map<String, String> masterMap = null; for (Map<String, String> temp : masterList){ if ("mymaster".equals(temp.get("name"))){ masterMap = temp; break; } } System.out.println("masterIp:" + masterMap.get("ip")); System.out.println("masterPort:" + masterMap.get("port")); List<String> mymaster = sentinelNode.sentinelGetMasterAddrByName("mymaster"); System.out.println("masterIp:" + mymaster.get(0)); System.out.println("masterPort:" + mymaster.get(1)); /** * 獲取slaves */ Jedis jedis = new Jedis("192.168.50.10", 26179); List<Map<String, String>> slaves = jedis.sentinelSlaves("mymaster"); for (Map<String, String> temp : slaves){ System.out.println("slaveIp:" + temp.get("ip")); System.out.println("slaveIp:" + temp.get("port")); } /** * 主從切換 */ Map<String, String> first = slaves.get(0); Jedis toBeMaster = new Jedis(first.get("ip"), Integer.parseInt(first.get("port"))); String flag = toBeMaster.slaveofNoOne(); // 將自己設定為主節點 for (int i = 1; i < slaves.size(); i++){ Map<String, String> temp = slaves.get(i); Jedis ortherSlave = new Jedis(temp.get("ip"), Integer.parseInt(temp.get("port"))); ortherSlave.slaveof(first.get("ip"), Integer.parseInt(first.get("port"))); } Jedis slave = new Jedis("192.168.50.10", 6279); /** * 重新載入配置資訊,復原 */ slave.configResetStat(); /** * 手動觸發RDB(save、bgsave)以及aof rewrite */ slave.save(); slave.bgsave(); slave.bgrewriteaof(); /** * 獲取資訊,可以知道自己是什麼角色 */ String info = slave.info(); String replication = slave.info("replication"); } }