redis系列--主從複製以及redis複製演進

1、前言

  在以前的文章已經詳細介紹了redis入門基礎已經持久化相關內容包括redis4.0所提供的混合持久化。html

  經過持久化功能,Redis保證了即便在服務器宕機狀況下數據的丟失很是少。可是若是這臺服務器出現了硬盤故障、系統崩潰等等,不只僅是數據丟失,極可能對業務形成災難性打擊。爲了不單點故障一般的作法是將數據複製多個副本保存在不一樣的服務器上,這樣即便有其中一臺服務器出現故障,其餘服務器依然能夠繼續提供服務。固然Redis提供了多種高可用方案包括:主從複製、哨兵模式的主從複製、以及集羣。node

  本文將詳細介紹Redis從2.6以到4.0提供複製方案的演進,也包括:主從複製、複製原理以及相關實踐。redis

2、主從複製

簡介

  在主從複製中,數據庫分爲兩類,一類是主庫(master),另外一類是同步主庫數據的從庫(slave)。主庫能夠進行讀寫操做,當寫操做致使數據變化時會自動同步到從庫。而從庫通常是隻讀的(特定狀況也能夠寫,經過參數slave-read-only指定),並接受來自主庫的數據,一個主庫可擁有多個從庫,而一個從庫只能有一個主庫。這樣就使得redis的主從架構有了兩種模式:一類是一主多從以下圖1,二類是「鏈式主從複製」--主->從->主-從以下圖2。數據庫

對於一主多從的複製架構沒必要多說,這裏解釋下鏈式主從複製:如上圖2,主庫A的數據會同步到從庫B和從庫C,而B又是從庫D和從庫E的主庫,因此B的數據會同步到從庫D和從庫E。若是向B中寫數據,數據只能同步到D和E中,因此對於這種架構數據的一致性將不能保持,也不推薦這種架構。緩存

 

搭建配置主從

  因爲沒有過多的機器,這裏將使用一臺機器上啓動多個redis實例實現主從複製。服務器

  對於redis來講搭建主從很是容易,引用官網一句話來講:there is a very simple to use and configure leader follower (master-slave) replication。網絡

  本次實踐分別以 10.1.210.68:6379 做爲主,兩個從服務器分別是 10.1.210.69:6380 和 10.1.210.69:6381架構

搭建步驟:運維

  1. 將redis.conf文件拷貝三份,名稱最好有顯示的區別,我這裏採用名字爲 6379.conf、 6380.conf、 6381.conf;
  2. 分別修改三個文件的ip(默認127.0.0.1能夠不用修改)、端口(儘可能和配置文件一致)、pid文件,日誌文件,持久化數據目錄(dir)、後臺運行(daemonize yes);
  3. 使用啓動命令腳本啓動每一個redis服務;
  4. 設置主從關係、驗證主從同步;

示例:socket

步驟一:

複製代碼
#創建三個redis目錄
mkdir -p /opt/db/{redis6379,redis6380,redis6381} 

#從源碼中拷貝配置文件
cp redis-stable/redis.conf /opt/db/redis6379/6379.conf
cp redis-stable/redis.conf /opt/db/redis6380/6380.conf 
cp redis-stable/redis.conf /opt/db/redis6381/6381.conf 
複製代碼

步驟二:

修改配置項以下:找到對應的參數修改便可,下面是每一個配置文件修改部分、本機器IP地址是10.1.210.69;

複製代碼
daemonize yes   #修改redis爲後臺運行模式

pidfile /var/run/redis_6379.pid  #修改運行的redis實例的pid,不能重複

logfile "/opt/db/redis6379/6379.log"  #指明日誌文件

dir "/opt/db/redis6379"   #工做目錄,存放持久化數據的目錄

bind 10.1.210.69   #監聽地址,若是是單機多個示例能夠不用修改

port 6379         #監聽端口,保持和配置文件名稱端口一致
複製代碼
6379.conf
複製代碼
daemonize yes   #修改redis爲後臺運行模式

pidfile /var/run/redis_6380.pid  #修改運行的redis實例的pid,不能重複

logfile "/opt/db/redis6380/6380.log"  #指明日誌文件

dir "/opt/db/redis6380"   #工做目錄,存放持久化數據的目錄

bind 10.1.210.69   #監聽地址,若是是單機多個示例能夠不用修改

port 6380         #監聽端口,保持和配置文件名稱端口一致
複製代碼
6380.conf
複製代碼
daemonize yes   #修改redis爲後臺運行模式

pidfile /var/run/redis_6381.pid  #修改運行的redis實例的pid,不能重複

logfile "/opt/db/redis6379/6381.log"  #指明日誌文件

dir "/opt/db/redis6381"   #工做目錄,存放持久化數據的目錄

bind 10.1.210.69   #監聽地址,若是是單機多個實例能夠不用修改使用127.0.0.1

port 6381         #監聽端口,保持和配置文件名稱端口一致
複製代碼
6381.conf

步驟三:

啓動每一個redis實例

redis-server /opt/db/redis6379/6379.conf
redis-server /opt/db/redis6380/6380.conf
redis-server /opt/db/redis6381/6381.conf

步驟四:

設置主從關係,固然你能夠直接指明從庫配置文件直接使用slaveof <masterip> <masterport>指定,這裏我在用客戶端修改,分別使用客戶端redis-cli命令連入端口爲6380、6381的redis。

連入6380數據庫,使用redis-cli -h 10.1.210.69 -p 6380,其中-h表明ip地址,-p表明端口,並執行slaveof 10.1.210.69 6379,並寫入配置文件config rewrite,以下:

一樣咱們在從庫6381執行相同操做:

此時咱們在使用info Replication 查看相關主從信息:

 

同時,還能夠測試主從功能,在6379上建立key,在從庫查看:

主庫:

從庫:

 

3、複製原理 

  瞭解redis複製原理對往後運維有很大幫助,包括如何規劃節點,如何處理節點故障,redis複製過程可分爲三個階段:

  1. 複製初始化階段
  2. 數據同步階段
  3. 命令傳播階段

 

複製初始化階段

  當執行完slaveof  masterip  port 命令時候,從庫根據指明的master節點ip和port向主庫發起socket鏈接,主庫收到socket鏈接以後將鏈接信息保存,此時鏈接創建;

  當socket鏈接創建完成之後,從庫向主庫發送ping命令,以確認主庫是否可用,此時的結果返回若是是PONG則表明主庫能夠用,不然可能出現超時或者主庫此時在處理其餘任務阻塞那麼此時從庫將斷開socket鏈接,而後進行重試;

  若是主庫鏈接設置了密碼,則從庫須要設置masterauth參數,此時從庫會發送auth命令,命令格式爲「auth + 密碼」進行密碼驗證,其中密碼爲masterauth參數配置的密碼,須要注意的是若是主庫設置了密碼驗證,從庫未配置masterauth參數則報錯,socket鏈接斷開。

  當身份驗證完成之後,從節點發送本身的監聽端口,主庫保存其端口信息,此時進入下一個階段:數據同步階段。

數據同步階段

  主庫和從庫都確認對方信息之後,即可開始數據同步,此時從庫向主庫發送psync命令(須要注意的是redis4.0版本對2.8版本的psync作了優化,後續會進行說明),主庫收到該命令後判斷是進行增量複製仍是全量複製,而後根據策略進行數據的同步,當主庫有新的寫操做時候,此時進入複製第三階段:命令傳播階段。

命令傳播階段

  當數據同步完成之後,在此後的時間裏主從維護着心跳檢查來確認對方是否在線,每隔一段時間(默認10秒,經過repl-ping-slave-period參數指定)主節點向從節點發送PING命令判斷從節點是否在線,而從節點每秒1次向主節點發送REPLCONF ACK命令命令格式爲:REPLCONF ACK {offset},其中offset指從節點保存的複製偏移量,做用一是彙報本身複製偏移量,主節點會對比複製偏移量向從節點發送未同步的命令,做用二在於判斷主節點是否在線,從庫接送命令並執行,最終實現與主庫數據相同。

樂觀複製

  redis採用量樂觀複製策略,容忍在必定時間內主從數據內容是不一樣的,可是二者的數據最終會同步。

 

4、redis複製演進

sync&psync1&psync2

  從redis2.6到4.0開發人員對其複製流程進行逐步的優化,如下是演進過程:

  • redis版本<=2.6<2.8,複製採用sync命令,不管是第一次主從複製仍是斷線重連進行復制都採用全量複製;
  • 2.8<=redis版本<4.0,複製採用psync,從redis2.8開始,redis複製從sync過渡到psync,這一特性主要添加了redis在斷線從新時候可以使用部分複製;
  • redis版本>=4.0,也採用psync,相比與2.8版本的psync優化了增量複製,這裏咱們稱爲psync2,2.8版本的psync稱爲psync1。

  如下將分別說明各個版本的複製演進。

sync

  在redis2.6以及之前的版本,複製採用sync命令,當一個從庫啓動後,會向主庫發送sync命令,主庫收到sync命令後執行bgsave後臺保存RDB快照(該過程在上一篇已經詳細介紹),同時將保存快照的將快照保存期間接受的寫命令保存到緩衝隊列。當快照完成之後,主庫將快照文件已經緩存的全部命令發送給從庫,從庫接受到快照文件並載入,再將執行主庫發送的命令,也就是上面咱們介紹的複製初始化階段和數據同步階段,其後就是命令增量同步,最終主庫與從庫保持數據一直。

  當從庫在某些狀況斷線重連(如從庫重啓、因爲網絡緣由主從鏈接超時),重複上述過程,進行數據同步。因而可知,redis2.6版本以及2.6之前複製過程所有采用全量複製。

  sync雖然解決了數據同步問題,可是在數據量比較大狀況下,從庫斷線歷來依然採用全量複製機制,不管是從數據恢復、寬帶佔用來講,sync所帶來的問題仍是不少的。因而redis從2.8開始,引入新的命令psync。

psync1

  在redis2.8版本,redis引入psync命令來進行主從的數據同步,這裏咱們稱該命令爲psync1。psync1實現依賴如下三個關鍵點:

  1.offset(複製偏移量):

  主庫和從庫分別各自維護一個複製偏移量(可使用info replication查看),用於標識本身複製的狀況,在主庫中表明主節點向從節點傳遞的字節數,在從庫中表明從庫同步的字節數。每當主庫向從節點發送N個字節數據時,主節點的offset增長N,從庫每收到主節點傳來的N個字節數據時,從庫的offset增長N。所以offset老是不斷增大,這也是判斷主從數據是否同步的標誌,若主從的offset相同則表示數據同步量,不通則表示數據不一樣步。如下圖示分別表明某個時刻兩個主從的同步狀況(如下是4.0版本截圖):

 

  

  2.replication backlog buffer(複製積壓緩衝區):

  複製積壓緩衝區是一個固定長度的FIFO隊列,大小由配置參數repl-backlog-size指定,默認大小1MB。須要注意的是該緩衝區由master維護而且有且只有一個,全部slave共享此緩衝區,其做用在於備份最近主庫發送給從庫的數據。

  在主從命令傳播階段,主節點除了將寫命令發送給從節點外,還會發送一份到複製積壓緩衝區,做爲寫命令的備份。除了存儲最近的寫命令,複製積壓緩衝區中還存儲了每一個字節相應的複製偏移量(以下圖),因爲複製積壓緩衝區固定大小先進先出的隊列,因此它老是保存的是最近redis執行的命令。

 

  3.run_id(服務器運行的惟一ID) 

  每一個redis實例在啓動時候,都會隨機生成一個長度爲40的惟一字符串來標識當前運行的redis節點,查看此id可經過命令info server查看。

  當主從複製在初次複製時,主節點將本身的runid發送給從節點,從節點將這個runid保存起來,當斷線重連時,從節點會將這個runid發送給主節點。主節點根據runid判斷可否進行部分複製:

  • 若是從節點保存的runid與主節點如今的runid相同,說明主從節點以前同步過,主節點會更具offset偏移量以後的數據判斷是否執行部分複製,若是offset偏移量以後的數據仍然都在複製積壓緩衝區裏,則執行部分複製,不然執行全量複製;
  • 若是從節點保存的runid與主節點如今的runid不一樣,說明從節點在斷線前同步的redis節點並非當前的主節點,只能進行全量複製;

 

  介紹完三個概念之後,接下來就能夠介紹redis2.8提供的psync命令實現過程,以下圖:

 

  圖文說明:

  • 若是從服務器之前沒有複製過任何主服務器,或者以前執行過SLAVEOF no one命令,那麼從服務器在開始一次新的複製時將向主服務器發送PSYNC ? -1命令,主動請求主服務器進行完整重同步(由於這時不可能執行部分重同步);
  • 相反地,若是從服務器已經複製過某個主服務器,那麼從服務器在開始一次新的複製時將向主服務器發送PSYNC <runid> <offset>命令:其中runid是上一次複製的主服務器的運行ID,而offset則是從服務器當前的複製偏移量,接收到這個命令的主服務器會經過這兩個參數來判斷應該對從服務器執行哪一種同步操做,如何判斷已經在介紹runid時進行詳細說明。

根據狀況,接收到PSYNC命令的主服務器會向從服務器返回如下三種回覆的其中一種:

  • 若是主服務器返回+FULLRESYNC <runid> <offset>回覆,那麼表示主服務器將與從服務器執行完整重同步操做:其中runid是這個主服務器的運行ID,從服務器會將這個ID保存起來,在下一次發送PSYNC命令時使用;而offset則是主服務器當前的複製偏移量,從服務器會將這個值做爲本身的初始化偏移量;
  • 若是主服務器返回+CONTINUE回覆,那麼表示主服務器將與從服務器執行部分同步操做,從服務器只要等着主服務器將本身缺乏的那部分數據發送過來就能夠了;
  • 若是主服務器返回-ERR回覆,那麼表示主服務器的版本低於Redis 2.8,它識別不了PSYNC命令,從服務器將向主服務器發送SYNC命令,並與主服務器執行完整同步操做。

   因而可知psync也有不足之處,當從庫重啓之後runid發生變化,也就意味者從庫仍是會進行全量複製,而在實際的生產中進行從庫的維護不少時候會進行重啓,而正是有因爲全量同步須要主庫執行快照,以及數據傳輸會帶不小的影響。所以在4.0版本,psync命令作了改進,如下說明。

psync2

  redis4.0新版本除了增長混合持久化,還優化了psync(如下稱psync2)並實現即便redis實例重啓的狀況下也能實現部分同步,下面主要介紹psync2實現過程。psync2在psync1基礎上新增兩個複製id(可以使用info replication 查看以下圖):

  • master_replid: 複製id1(後文簡稱:replid1),一個長度爲41個字節(40個隨機串+’0’)的字符串,每一個redis實例都有,和runid沒有直接關聯,但和runid生成規則相同。當實例變爲從實例後,本身的replid1會被主實例的replid1覆蓋。

  • master_replid2:複製id2(後文簡稱:replid2),默認初始化爲全0,用於存儲上次主實例的replid1。

 

  在4.0以前的版本,redis複製信息徹底丟失,因此每一個實例重啓後只能進行全量複製,到了4.0版本,任然可使用部分同步,其實現過程:

第一步:存儲複製信息

  redis在關閉時,經過shutdown save,都會調用rdbSaveInfoAuxFields函數,把當前實例的repl-id和repl-offset保存到RDB文件中,當前的RDB存儲的數據內容和複製信息是一致性的可經過redis-check-rdb命令查看。

第二步:重啓後加載RDB文件中的複製信息

  redis加載RDB文件,會專門處理文件中輔助字段(AUX fields)信息,把其中repl_id和repl_offset加載到實例中,分別賦給master_replid和master_repl_offset兩個變量值,特別注意當從庫開啓了AOF持久化,redis加載順序發生變化優先加載AOF文件,可是因爲aof文件中沒有複製信息,因此致使重啓後從實例依舊使用全量複製!

第三步:向主庫上報復制信息,判斷是否進行部分同步

  從實例向主庫上報master_replid和master_repl_offset+1;從實例同時知足如下兩條件,就能夠部分從新同步,不然執行全量同步:

  • 從實例上報master_replid串,與主實例的master_replid1或replid2有一個相等,用於判斷主從未發生改變;
  • 從實例上報的master_repl_offset+1字節,還存在於主實例的複製積壓緩衝區中,用於判斷從庫丟失部分是否在複製緩衝區中;

 

psync2除了解決redis重啓使用部分同步外,還爲解決在主庫故障時候從庫切換爲主庫時候使用部分同步機制。redis從庫默認開啓複製積壓緩衝區功能,以便從庫故障切換變化master後,其餘落後該從庫能夠從緩衝區中獲取缺乏的命令。該過程的實現經過兩組replid、offset替換原來的master runid和offset變量實現:

  • 第一組:master_replid和master_repl_offset:若是redis是主實例,則表示爲本身的replid和複製偏移量; 若是redis是從實例,則表示爲本身主實例的replid1和同步主實例的複製偏移量。
  • 第二組:master_replid2和second_repl_offset:不管主從,都表示本身上次主實例repid1和複製偏移量;用於兄弟實例或級聯複製,主庫故障切換psync。

判斷是否使用部分複製條件:若是從庫提供的master_replid與master的replid不一樣,且與master的replid2不一樣,或同步速度快於master; 就必須進行全量複製,不然執行部分複製。

如下常見的主從切換均可以使用部分複製:

  1. 一主一從發生切換,A->B 切換變成 B->A ;
  2. 一主多從發生切換,兄弟節點變成父子節點時;
  3. 級別複製發生切換, A->B->C 切換變成 B->C->A;

用一句redis開發者話來講psync2,儘管它不是很是完美,可是已經很是適用。

 

5、立刻實踐

  爲了演示4.0的psync2功能,這裏作實踐演示。主從實例採用上述搭建的主從架構,主庫:10.1.210.69:6379 、從庫:10.1.210.69:6380和10.1.210.69:6381。首先關閉一臺從節點10.1.210.69:6380:

查看日誌從庫執行了RDB快照: 

查看RDB文件,裏面記錄了相關複製信息:

此時咱們在啓動從庫,查看對應日誌,此時啓用部分複製來恢復數據:

以前提到過,當從庫開啓來AOF持久化時候,重啓時加載文件從AOF文件中加載,可是AOF文件中沒有對應的複製信息,因此從實例依舊採用全量複製。如下是從庫開啓AOF持久化後,重啓日誌,能夠看到是全量同步:

 

6、總結 

複製演進中解決的問題

  早起版本才用的sync同步方法,雖然實現了簡單的主從同步,可是在從庫斷線或重啓時沒法實現部分同步,由此在2.8版本推出psync1,redis2.8的部分同步機制,有效解決了網絡環境不穩定、redis執行高時間複雜度的命令引發的複製中斷,從而致使全量同步。但在應對從庫重啓和主庫故障切換的場景時,psync1仍是需進行全量同步。因而在4.0新的psync獲得了增強,redis4.0經過在關閉時候執行RDB快照,將複製信息保存在RDB中等到從新啓動時加載RDB文件載入複製信息,經過對比複製信息啓用部分複製,有效的解決了psync1情形下從庫重啓複製信息丟失而致使全量同步的問題。同時引入兩組replid、offset,主從切換時交換兩組值來實現主從故障切換時候依舊採用部分複製。

  再次強調,在上述的過程的實現是從庫不開啓AOF持久化狀況下,若是從庫開啓的AOF持久化,重啓時候依然使用全量複製。

 

故障切換 

  在實際生產環境中,在沒有哨兵的主從架構裏若是要重啓從庫,比較好的方式是先動態調配主庫中的複製積壓緩衝隊列,調整大小應大於某個N值,N值計算公式:backlog_size = 重啓從實例時長 * 主實例offset每秒寫入量,這樣好處在於,即便從庫重啓斷線一段時間後再啓動任然保持部分複製。調整方式經過config set repl-backlog-size <字節數>,當咱們重啓完成後又能夠將

repl-backlog-size從新調回原有值。固然在數據量小或者重啓時間短狀況下,也能夠直接重啓從節點。 

  當主庫宕機時候,在沒有哨兵狀況下,須要現將從節點中的某一臺提高爲主庫,咱們須要在全部從節點中找到當前的offset最大值的從庫(這樣表明該庫相對其餘從庫數據較全),而後執行slaveof no one將該從庫提高爲主庫,最後將全部其餘重庫依次執行slaveof ip port(ip和port是新主庫的IP地址和端口),最後完成故障切換。此外,redis4.0中這種切換任然採用部分複製進行數據同步。

 

主從配置參數

複製代碼
########從庫##############

slaveof <masterip> <masterport> 
#設置該數據庫爲其餘數據庫的從數據庫

masterauth <master-password>
#主從複製中,設置鏈接master服務器的密碼(前提master啓用了認證)

slave-serve-stale-data yes
# 當從庫同主庫失去鏈接或者複製正在進行,從庫有兩種運行方式:
# 1) 若是slave-serve-stale-data設置爲yes(默認設置),從庫會繼續相應客戶端的請求
# 2) 若是slave-serve-stale-data設置爲no,除了INFO和SLAVOF命令以外的任何請求都會返回一個錯誤"SYNC with master in progress"

slave-priority 100
#當主庫發生宕機時候,哨兵會選擇優先級最高的一個稱爲主庫,從庫優先級配置默認100,數值越小優先級越高

slave-read-only yes
#從節點是否只讀;默認yes只讀,爲了保持數據一致性,應保持默認


####主庫##############

repl-disable-tcp-nodelay no
#在slave和master同步後(發送psync/sync),後續的同步是否設置成TCP_NODELAY假如設置成yes,則redis會合並小的TCP包從而節省帶寬,但會增長同步延遲(40ms),形成master與slave數據不一致假如設置成no,則redis master會當即發送同步數據,沒有延遲
#前者關注性能,後者關注一致性

repl-ping-slave-period 10
#從庫會按照一個時間間隔向主庫發送PING命令來判斷主服務器是否在線,默認是10秒

repl-backlog-size 1mb
#複製積壓緩衝區大小設置

repl-backlog-ttl 3600
#master沒有slave一段時間會釋放複製緩衝區的內存,repl-backlog-ttl用來設置該時間長度。單位爲秒。

min-slaves-to-write 3
min-slaves-max-lag 10
#設置某個時間斷內,若是從庫數量小於該某個值則不容許主機進行寫操做,以上參數表示10秒內若是主庫的從節點小於3個,則主庫不接受寫請求,min-slaves-to-write 0表明關閉此功能。
複製代碼

 

 

轉載自:

https://www.cnblogs.com/wdliu/p/9407179.html

相關文章
相關標籤/搜索