1. 程式人生 > >Redis 主從複製 原理與用法

Redis 主從複製 原理與用法

Redis 複製功能的幾個重要方面:
1. 一個Master可以有多個Slave;
2. Redis使用非同步複製。從2.8開始,Slave會週期性(每秒一次)發起一個Ack確認複製流(replication stream)被處理進度;
3. 不僅主伺服器可以有從伺服器, 從伺服器也可以有自己的從伺服器, 多個從伺服器之間可以構成一個圖狀結構;
4. 複製在Master端是非阻塞模式的,這意味著即便是多個Slave執行首次同步時,Master依然可以提供查詢服務;
5. 複製在Slave端也是非阻塞模式的:如果你在redis.conf做了設定,Slave在執行首次同步的時候仍可以使用舊資料集提供查詢;你也可以配置為當Master與Slave失去聯絡時,讓Slave返回客戶端一個錯誤提示;
6. 當Slave要刪掉舊的資料集,並重新載入新版資料時,Slave會阻塞連線請求(一般發生在與Master斷開重連後的恢復階段);
7. 複製功能可以單純地用於資料冗餘(data redundancy),也可以通過讓多個從伺服器處理只讀命令請求來提升擴充套件性(scalability): 比如說, 繁重的 SORT 命令可以交給附屬節點去執行。
8. 可以通過修改Master端的redis.config來避免在Master端執行持久化操作(Save),由Slave端來執行持久化。

Redis複製工作原理:


1. 如果設定了一個Slave,無論是第一次連線還是重連到Master,它都會發出一個SYNC命令;
2. 當Master收到SYNC命令之後,會做兩件事:
a) Master執行BGSAVE,即在後臺儲存資料到磁碟(rdb快照檔案);
b) Master同時將新收到的寫入和修改資料集的命令存入緩衝區(非查詢類);
3. 當Master在後臺把資料儲存到快照檔案完成之後,Master會把這個快照檔案傳送給Slave,而Slave則把記憶體清空後,載入該檔案到記憶體中;
4. 而Master也會把此前收集到緩衝區中的命令,通過Reids命令協議形式轉發給Slave,Slave執行這些命令,實現和Master的同步;
5. Master/Slave此後會不斷通過非同步方式進行命令的同步,達到最終資料的同步一致;
6. 需要注意的是Master和Slave之間一旦發生重連都會引發全量同步操作。但在2.8之後版本,也可能是部分同步操作。

部分複製
2.8開始,當Master和Slave之間的連線斷開之後,他們之間可以採用持續複製處理方式代替採用全量同步。
Master端為複製流維護一個記憶體緩衝區(in-memory backlog),記錄最近傳送的複製流命令;同時,Master和Slave之間都維護一個複製偏移量(replication offset)和當前Master伺服器ID(Master run id)。當網路斷開,Slave嘗試重連時:
a. 如果MasterID相同(即仍是斷網前的Master伺服器),並且從斷開時到當前時刻的歷史命令依然在Master的記憶體緩衝區中存在,則Master會將缺失的這段時間的所有命令傳送給Slave執行,然後複製工作就可以繼續執行了;
b. 否則,依然需要全量複製操作;

Redis 2.8 的這個部分重同步特性會用到一個新增的 PSYNC 內部命令, 而 Redis 2.8 以前的舊版本只有 SYNC 命令, 不過, 只要從伺服器是 Redis 2.8 或以上的版本, 它就會根據主伺服器的版本來決定到底是使用 PSYNC 還是 SYNC :

如果主伺服器是 Redis 2.8 或以上版本,那麼從伺服器使用 PSYNC 命令來進行同步。
如果主伺服器是 Redis 2.8 之前的版本,那麼從伺服器使用 SYNC 命令來進行同步。


Redis 複製機制

首先是slave端,對於slave端來說,主從複製主要經歷四個階段:

第一階段:與master建立連線
第二階段:向master發起同步請求(SYNC)
第三階段:接受master發來的RDB資料
第四階段:載入RDB檔案

下面我們就通過一個圖來概述在每一個階段中,slave究竟做了些什麼:



關於上圖,有一點說明下:redis接收到slaveof master_host master_port命令後並沒有馬上與master建立連線,而是當執行伺服器例行任務serverCron,發現自己正處於REDIS_REPL_CONNECT狀態,這時才真正的向maser發起連線,虛擬碼:

Python程式碼  收藏程式碼
  1. def serverCron():  
  2.     # 伺服器處於REDIS_REPL_CONNECT狀態
  3.     if redisServer.repl_state == REDIS_REPL_CONNECT:  
  4.         # 向master發起連線
  5.         connectWithMaster()  
  6.     # 其他例行任務(省略)...


接著我們來看下主從複製過程中,master這邊的流程是如何,在具體看細節之前,我們先綜合來看master這邊主要做的幾件事情:



看完這個圖,你也許會有以下幾個疑問:

1. 為什麼在master傳送完RDB檔案後,還要定期的向slave傳送PING命令?
2. 在傳送完RDB檔案之後,master傳送的“變更”命令又是什麼,有什麼用?

在回答問題之前1,我們先回答問題2:
master儲存RDB檔案是通過一個子程序進行的,所以master依然可以處理客戶端請求而不被阻塞,但這也導致了在儲存RDB檔案期間,“鍵空間”可能發生變化(譬如接收到一個客戶端請求,執行"set name diaocow"命令),因此為了保證資料同步的一致性,master會在儲存RDB檔案期間,把接受到的這些可能變更資料庫“鍵空間”的命令儲存下來,然後放到每個slave的回覆列表中,當RDB檔案傳送完master會發送這些回覆列表中的內容,並且在這之後,如果資料庫發生變更,master依然會把變更的命令追加到回覆列表傳送給slave,這樣就可以保證master和slave資料的一致性!相關虛擬碼:

Python程式碼  收藏程式碼
  1. def processCommand(cmd, argc, argv):  
  2.     # 處理命令
  3.     call(cmd, argc, argv)  
  4.     # 如果該命令造成資料庫鍵空間變化and當前redis是一個master,則同步變更命令
  5.     if redisServer.update_key_space and len(redisServer.slaves) > 0:  
  6.         replicationFeedSlaves(cmd, argc, argv)  
  7. def replicationFeedSlaves(cmd, argc, argv):   
  8.     # 把變更命令傳送給每一個處於:REDIS_REPL_WAIT_BGSAVE_END狀態的slave節點
  9.     for slave in redisServer.slaves:  
  10.         if slave.replstate == REDIS_REPL_WAIT_BGSAVE_START:  
  11.             continue
  12.         slave.updateNotify(cmd, argc, argv)  

由於在傳送完RDB檔案之後,master會不定時的給slave傳送“變更”命令,可能過1s,也可能過1小時,所以為了防止slave無意義等待(譬如master已經掛掉的情況),master需要定時傳送“保活”命令PING,以此告訴slave:我還活著,不要中斷與我的連線

現在我們就看下,當master接受到slave傳送的sync同步命令後究竟發生了哪些事:


上圖看似分支複雜,但我們抓住以下幾點即可:

1.儲存RDB檔案是在一個子程序中進行的;
2.如果master已經在儲存RDB檔案,但是沒有客戶端正在等待這次BGSAVE,新新增的slave需要等到下次BGSAVE,而不能直接使用這次生成的RDB檔案(原因圖中已經說明)
3.master會定期檢查RDB檔案是否儲存完畢(時間事件serverCron);

接下來我們看下,master是如何給每一個slave傳送RDB檔案的:



好了,至此我們已經分析完在主從複製過程中,master和slave兩邊分別是怎麼一個處理流程;最後,我繪製了一個圖,綜述了主從複製這一過程(我們可以邊看圖,邊回憶其中的具體細節):



PS:在主從複製過程中,任何一步發生錯誤,都會導致整個過程重頭開始,所以若RDB檔案很大又或是此時正處在業務高峰期,對系統性能將會有非常大的影響!


Redis 複製操作

一、Redis實現複製很簡單,主要有下面兩個方法
1、從機器的redis.conf新增slaveof 主IP 埠,然後帶上配置檔案啟動server
# src/redis-server redis.conf
2、從機器啟動後在命令裡打入slaveof 主IP 埠,這裡是全域性命令
127.0.0.1 6379> salveof 192.168.10.10 6379

二、關閉同步(從機器)
# src/redis-server no one

注意:
1,Redis不支援主主複製,任意兩臺機器間不能互相slaveof

2,從設定同步時,會清空所有資料