知識點回顧:
通過上一節課我們初步接觸到了一小部分的docker命令的使用、和redis集群有哪些方式,重點是將到了主從集
群,並通過實例逐步教給大家如何實現一組多從的「主從(Master-Slave)集群模式」,我們的實現結果已經完
成,今天我們來一起學習探討下redis-主從複製原理和部分問題的解決方法,學習本課程前,請學習者務必完成。

1、了解下主從複製的原理和複製的過程:

現在我們有了上節課搭建主從複製集群的能力,假如我們現在就運用在我們的項目中,因為網路原因和平台數據的錯綜複雜的交互,難免會遇到一些問題,不管什麼情況下我們都不能永遠保證我們所部署的系統或架構不會出現任何問題。因此,為了能夠解決這些常見的問題,對主從的工作原理和數據處理的過程是我們必須必須得掌握的。話不多說,我們先來看一張圖,來幫助大家去理解。

從上面的結構圖中我們不難發現主從複製的過程具體分為三個階段:1、準備階段開始建立連接;2、同步數據;3、傳播命令和六個過程

現在我們來詳細說下這6個過程:

一、準備階段開始建立連接:

這裡就是執行slaveof命令以後,從節點開始嘗試與主節點進行socket連接。

1.保存主節點Master數據信息:

主要是保存MasterHost、MasterPort兩個欄位也就是我們常說的ip地址和埠號【上節課我們配置從節點時,已經告訴大家了,學過上節課的內容大家應該都有所了解】這裡我們在從節點執行下slaveof命令來連接主節點時日誌就會幫我們記錄這樣的過程

在從節點slave 執行以下命令去查看日誌信息:
[root@90e5a51dda7f /]# cat /var/log/redis/redis.log
你會看到有下面的截圖內容

注意:slaveof是非同步命令,當從節點完成與主節點連接時會先想客戶端返回成功ok後再次進行複製數據【面試中可能會遇到】

2.建立socket連接:

從節點每秒調用複製定時函數replicationCron()來維護複製相關邏輯,當發現有新的主節點可以連接時,便會根據主節點的ip和埠號進行嘗試建立連接。從節點:為該socket建立一個專門處理複製工作的文件事件處理器,負責後續的複製工作(從節點會建立一個 socket 套接字)。主節點:接收到從節點的socket連接後,為該socket創建相應的客戶端狀態(我們稱之為「即時狀態」),並將從節點看做是連接到主節點的一個客戶端,後面的步驟會以從節點向主節點發送命令請求的形式來進行,也就是說後續的複製動作請求一切都是來自從節點。看下日誌截圖:

3.發送ping命令:

當從節點成為主節點的客戶端後,從節點會發送ping進行連接後首次通信,其中ping的主要目的是:

  • 檢測主從之間的socket連接(網路套接字)是否可用。
  • 檢測主節點當前是否可接受處理命令,有兩種情況:1、超時:一定的時間嘗試連接後,如果從節點一直未收到主節點的socket成功連接的信息反饋結果,說明socket連接不可用則會自動斷開並嘗試重連。2、如果主節點在處理其他命令,未能處理從節點請求的命令時同樣說明socket連接不可用會自動斷開並嘗試重連。【主要就是如果發送 ping 命令後,從節點沒有收到主節點的 pong 回復或者超時,比如網路超時或者主節點正在阻塞無法響應命令,從節點會斷開複製連接,下次定時任務會發起重連。】

當主節點返回pong的情況時,說明socket連接成功

請看下面的圖來深化我們的理解:

4、身份驗證:

如果主節點設置了 requirepass 參數,則需要密碼驗證,從節點必須配置 masterauth 參數保證與主節點相同的密碼才能通過驗證;如果驗證失敗複製將終止,從節點重新發起複制流程。

二、數據同步階段:

數據同步階段是主從複製的核心階段,根據主從的狀態不同而複製又分為「全量複製」和「部分複製」兩種,現在我們要理清主從節點之間的關係,在數據同步之前,都是從節點向主節點發起命令請求進行socket連接,連接失敗則返回輪詢重試連接直到連接成功為止。而在同步數據之後,則主節點是從節點的客戶端,開始主機點發起命令請求將寫數據推送到從節點的緩衝區進行複製的動作。

5、同步數據:

主從複製socket連接正常通信後,對於首次建立複製的場景,主節點會把持有的數據全部發送給從節點,這部分操作是耗時最長的步驟;因為數據很龐大的話,會執行大量的數據複製動作,所以會很耗時。

三、傳播命令複製階段:

數據同步階段完成後,主從節點進入命令傳播階段;在這個階段主節點將自己執行的寫命令發送給從節點,從節點接收命令並執行,從而保證主從節點數據的一致性。

注意:首先主從複製中的命令傳播是非同步的過程,即主節點開始想從節點發起寫的命令,並不會等待從節點的回復,因此常說到主從節點數據的一致性其實是很難保證的。

6、數據複製:

當主節點把當前的數據同步給從節點後,便完成了複製的建立流程。接下來就是主節點會持續地把數據以寫命令的方式發送給從節點,保證主從數據一致性。當我們在主節點寫入數據時,我們可以看下從節點的複製動作日誌信息:

2、【數據同步階段】全量複製和部分複製:

講到這裡,我們來做一個比較簡單的實驗也是對大家知識點的擴充,我們寫個shell腳本來做批量的寫動作,這樣做的目的是為了我們的redis緩存數據足夠多,實驗起來會比較方便,效果的數據可視化能讓大家更全面地去理解,全量複製和部分複製的區別,以及問題的解決方向,這裡就不再跟大家詳細講解linux的shell腳本的命令了【這裡我們運行存儲十萬條redis數據】。如果想學學linux基礎請下載下面給大家提供的「鳥哥私房菜」高清PDF的版本

pan.baidu.com/s/1x2fvZA

密碼:d1ob

執行以下命令
[root@instance-rttngj1u ~]# docker exec -it redis-master bash
[root@90e5a51dda7f /]# touch redis.sh&vi redis.sh
#!/bin/bash
for((i=1;i<100000;i++))
do
redis-cli -h 172.60.0.2 -p 6379 set slave${i} ${i}
done
再推出保存執行腳本
[root@90e5a51dda7f /]# sh redis.sh
這時我們將會看到很多的ok存儲結果集反饋並顯示。
【下一節課需要用】

上面的命令執行完畢後,我們來介紹下什麼是「全量複製」,什麼是「部分複製」以及「複製偏移量」、「複製積壓緩衝區」的概念:

一、全量複製:

當我們第一次構建大量的數據集或者是初次複製以及其它無法進行部分複製的情況,將主節點中的所有數據都發送給從節點,就是主節點有多少數據統統地複製給從節點,這個過程是一個非常重型的操作,如果數據量較大時,會消耗很多的資源空間的同時對主從節點和網路也會造成很大的開銷,由此可見,全量複製的變態性行為。

二、部分複製:

部分複製要比全量複製人性化一點,少什麼數據就補什麼數據,常常用來處理在主從複製中因網路閃斷等原因造成的數據丟失場景,進而當從節點再次連上主節點後,只要條件允許,主節點就會補發丟失的數據給從節點。因此這種部分補發的數據遠遠小於全量數據,可以有效避免全量複製的過高開銷,但是需要注意的是,如果我們的網路中斷時間過長,導致主節點斷開了連接致使主節點沒有能夠完整地保存中斷期間執行的寫命令,則就無法進行部分複製,任然會去使用全量複製。

三、複製偏移量:

不管何時參與複製的主從節點都會維護自身複製偏移量。主節點(master)在處理完寫入命令後,會把命令的位元組長度做累加記錄,我們可以在redis客戶端執行命令來查看下主節點的數據統計信息master_repl_offset指標

這裡我們不難發現三個從節點的offset與主節點的master_repl_offset偏移量數據信息是相同的,是因為從節點會每秒鐘都會向主節點發起命令給主節點上報自己的複製偏移量,而主節點在接受命令後,會記錄從節點複製偏移量的數值。

同時從節點在接受到主節點發起的命令後,也會累加並記錄自己的複製偏移量數值,其目的我們不難得知就是為了複製主節點的數據信息實現數據的一致性。

四、複製積壓緩衝區,什麼是緩存區,緩存區有幾個呢,作用又是什麼?

答:複製積壓緩衝區是保存在主節點上的一個固定長度的隊列,默認大小為1MB,作用就是當主節點開始有從節點的創建時,積壓緩衝區就會備份主節點最近發給從節點的數據信息。這裡要注意的是無論主節點有一個還是多個從節點,都只需要一個複製積壓緩衝區。在運行複製的第三個階段也就是「命令傳播階段」中,主節點除了將寫命令發送給從節點,還會發送一份給複製積壓緩衝區,作為寫命令的備份。

除了存儲寫命令,複製積壓緩衝區中還存儲了其中的每個位元組對應的複製偏移量(offset)。不管是主節點還是從節點都用複製偏移量的數據信息,可以說複製偏移量就是複製動作的拐點。由於複製積壓緩衝區的大小長度是一定的且是先進先出【保存最近新的執行命令,當達到一定額度值會把舊的執行命令擠出緩衝區】。記住一點就是「保新棄舊」。由於該緩衝區長度固定且有限,因此可以備份的寫命令也有限,當主從節點複製偏移量offset的差距過大超過緩衝區長度時,將無法執行部分複製,只能執行全量複製。當然為了提高網路中斷時部分複製執行的概率,可以根據需要增大複製積壓緩衝區的大小(通過配置repl-backlog-size);例如如果網路中斷的平均時間是60s,而主節點平均每秒產生的寫命令(特定協議格式)所佔的位元組數為100KB,則複製積壓緩衝區的平均需求為6MB,保險起見,可以設置為12MB,來保證絕大多數斷線情況都可以使用部分複製。目的就是儘可能的杜絕「全量複製」。

注意:從節點將offset發送給主節點後,主節點根據offset和緩衝區大小決定能否執行部分複製:

  • 如果offset偏移量之後的數據,仍然都在複製積壓緩衝區里,則執行部分複製;
  • 如果offset偏移量之後的數據已不在複製積壓緩衝區中(數據已被擠出),則執行全量複製。

五、圖解:

以上講到了全量複製、部分複製和複製擠壓緩衝區,下面我們繼續結合圖片來理解:

①、全量複製圖解:

複製的全過程解釋:

  1. Redis 內部會發出一個同步命令,剛開始是 Psync 命令,Psync ? -1表示要求 master 主機同步數據。
  2. 主機會向從機發送 runid 和 offset,因為 slave 並沒有對應的 offset,所以是全量複製。
  3. 從機 slave 會保存 主機master 的基本信息 save masterInfo。
  4. 主節點收到全量複製的命令後,執行bgsave(非同步執行),在後台生成RDB文件(快照),並使用一個緩衝區(稱為複製緩衝區)記錄從現在開始執行的所有寫命令。
  5. 主機send RDB 發送 RDB 文件給從機。
  6. 發送緩衝區數據。
  7. 刷新舊的數據,從節點在載入主節點的數據之前要先將老數據清除。
  8. 載入 RDB 文件將資料庫狀態更新至主節點執行bgsave時的資料庫狀態和緩衝區數據的載入

總結全量複製的開銷:

  • bgsave 時間
  • RDB 文件網路傳輸時間
  • 從節點清空數據的時間
  • 從節點載入 RDB 的時間

②、部分複製:

複製的全過程解釋:

  1. 如果網路抖動(連接斷開 connection lost)。
  2. 主機master 還是會寫 replbackbuffer(複製緩衝區)。
  3. 從機slave 會繼續嘗試連接主機。
  4. 從機slave 會把自己當前 runid 和偏移量傳輸給主機 master,並且執行 pysnc 命令同步。
  5. 如果 master 發現你的偏移量是在緩衝區的範圍內,就會返回 continue 命令。
  6. 同步了 offset 的部分數據,所以部分複製的基礎就是偏移量 offset。

六、伺服器運行的runid:

每個Redis節點(無論主從),在啟動時都會自動生成一個隨機ID(每次啟動都不一樣),由40個隨機的十六進位字元組成;runid用來唯一識別一個Redis節點。 通過info server命令,可以查看節點的runid。如圖:

主從節點初次複製時,主節點將自己的runid發送給從節點,從節點將這個runid保存起來;當斷線重連時,從節點會將這個runid發送給主節點;主節點根據runid判斷能否進行部分複製:如果從節點保存的runid與主節點現在的runid相同,說明主從節點之前同步過,主節點會繼續嘗試使用部分複製(到底能不能部分複製還要看offset和複製積壓緩衝區的情況)如果從節點保存的runid與主節點現在的runid不同,說明從節點在斷線前同步的Redis節點並不是當前的主節點,只能進行全量複製。

3.主從複製常用的相關配置:

從節點配置:

  • slaveof <masterip> <masterport>:slave實例需要配置該項,指向master的(ip, port)。
  • masterauth <master-password>:如果master實例啟用了密碼保護,則該配置項需填master的啟動密碼;若master未啟用密碼,該配置項需要注釋掉
  • slave-serve-stale-data:指定slave與master連接中斷時的動作。默認為yes,表明slave會繼續應答來自client的請求,但這些數據可能已經過期(因為連接中斷導致無法從master同步)。若配置為no,則slave除正常應答"INFO"和"SLAVEOF"命令外,其餘來自客戶端的請求命令均會得到"SYNC with master in progress"的應答,直到該slave與master的連接重建成功或該slave被提升為master。
  • slave-read-only:指定slave是否只讀,默認為yes。若配置為no,這表示slave是可寫的,但寫的內容在主從同步完成後會被刪掉。
  • repl-disable-tcp-nodelay:指定向slave同步數據時,是否禁用socket的NO_DELAY選項。若配置為yes,則禁用NO_DELAY,則TCP協議棧會合併小包統一發送,這樣可以減少主從節點間的包數量並節省帶寬,但會增加數據同步到slave的時間。若配置為no,表明啟用NO_DELAY,則TCP協議棧不會延遲小包的發送時機,這樣數據同步的延時會減少,但需要更大的帶寬。通常情況下,應該配置為no以降低同步延時,但在主從節點間網路負載已經很高的情況下,可以配置為yes。
  • slave-priority:指定slave的優先順序。在不只1個slave存在的部署環境下,當master宕機時,Redis Sentinel會將priority值最小的slave提升為master。需要注意的是,若該配置項為0,則對應的slave永遠不會被Redis Sentinel自動提升為master。

4、主從複製常見問題解決:

  • 讀寫分離
  • 主從配置不一致
  • 規避全量複製
  • 規避複製風暴

一、讀寫分離:

讀流量分攤到從節點。這是個非常好的特性,如果一個業務只需要讀數據,那麼我們只需要連一台 slave 從機讀數據。

我們都知道雖然讀寫有優勢,能夠讓讀這部分分配給各個 slave 從機,如果不夠,直接加 slave 機器,

但是也會出現以下問題:

1、複製數據延遲:

可能會出現 slave 延遲導致讀寫不一致等問題,當然你也可以使用監控偏移量 offset,如果 offset 超出範圍就切換到 master 上,邏輯切換,而具體延遲多少,可以通過 info replication 的 offset 指標進行排查。

對於無法容忍大量延遲場景,可以編寫外部監控程序(比如consul)監聽主從節點的複製偏移量,當延遲較大時觸發報警或者通知客戶端避免讀取延遲過高的從節點。

模擬網路延遲
docker run --privileged -itd -v /usr/local/redis/conf:/conf --name redis-slave4 --net mynetwork -p 6384:6379 --ip 172.60.0.4 redis2
--privileged Docker容器將擁有訪問主機所有設備的許可權

通過linux下的控流工具,模擬網路延遲,用代碼模擬下,因為對於網路的操作屬於特殊許可權所以需要添加 --privileged 參數
yum install iproute
配置延遲5s
tc qdisc add dev eth0 root netem delay 5000ms
tc qdisc del dev eth0 root netem delay 5000ms
同時從節點的slave-serve-stale-data參數也與此有關,
它控制這種情況下從節點的表現:如果為yes(默認值),
則從節點仍能夠響應客戶端的命令;如果為no,則從節點只能響應info、slaveof等少數命令。
該參數的設置與應用對數據一致性的要求有關;如果對數據一致性要求很高,則應設置為no。

2、只有N個從節點鏈接的時候才允許寫入:

因為Redis使用的是非同步複製,所以沒有辦法保證從節點確實收到的給定的寫入請求,所以存在一個窗口期的數據丟失的可能性。從節點每秒鐘都會ping主節點,告知它所有的複製流工作了。主節點會記住從每個從節點收到的最新的ping用戶可以給主節點配置一個在等於從節點的數量的最低值和不超過最高值之間的延遲如果至少有N個從節點,如果少於延遲M秒,則寫入將被接受。你可能覺得經最大努力保證數據安全的機制,雖然數據一致性無法保證,但是至少只是幾秒的時間窗口內丟失數據。通常情況下範圍數據丟失可比無範圍數據丟失好多了。如果條件不滿足,主節點將會返回一個錯誤,並且寫入請求將不被接受

主機配置兩個參數:

min-slaves-to-write <number of slaves>
min-slaves-max-lag <number of seconds>

3、如何選擇,要不要讀寫分離:

沒有最合適的方案,只有最合適的場景,讀寫分離需要業務可以容忍一定程度的數據不一致,適合讀多寫少的業務場景,讀寫分離,是為了什麼,主要是因為要建立一主多從的架構,才能橫向任意擴展slave node去支撐更大的讀吞吐量。

4、從節點故障問題:

對於從節點的故障問題,需要在客戶端維護一個可用從節點可用列表,當從節點故障時,立刻切換到其他從節點或主節點,之後講解redis Cluster 時候可以解決這個問題

5、配置不一致:

主機和從機不同,經常導致主機和從機的配置不同,並帶來問題。 數據丟失:主機和從機有時候會發生配置不一致的情況,例如 maxmemory 不一致,如果主機配置 maxmemory 為8G,從機 slave 設置為4G,這個時候是可以用的,而且還不會報錯。但是如果要做高可用,讓從節點變成主節點的時候,就會發現數據已經丟失了,而且無法挽回。

6、規避全量複製:

全量複製指的是當 slave 從機斷掉並重啟後,runid 產生變化而導致需要在 master 主機里拷貝全部數據。這種拷貝全部數據的過程非常耗資源。全量複製是不可避免的,例如第一次的全量複製是不可避免的,這時我們需要選擇小主節點,且maxmemory 值不要過大,這樣就會比較快。同時選擇在低峰值的時候做全量複製。

造成全量複製的原因有:

  • 是主從機的運行 runid 不匹配。解釋一下,主節點如果重啟,runid 將會發生變化。如果從節點監控到 runid 不是同一個,它就會認為你的節點不安全。當發生故障轉移的時候,如果主節點發生故障,那麼從機就會變成主節點。我們會在後面講解哨兵和集群。
  • 複製緩衝區空間不足,比如默認值1M,可以部分複製。但如果緩存區不夠大的話,首先需要網路中斷,部分複製就無法滿足。其次需要增大複製緩衝區配置(relbacklogsize),對網路的緩衝增強。參考之前的說明。

解決方案:

在一些場景下,可能希望對主節點進行重啟,例如主節點內存碎片率過高,或者希望調整一些只能在啟動時調整的參數。如果使用普通的手段重啟主節點,會使得runid發生變化,可能導致不必要的全量複製。為了解決這個問題,Redis提供了debug reload的重啟方式:重啟後,主節點的runid和offset都不受影響,避免了全量複製。

  • 當一個主機下面掛了很多個 slave 從機的時候,主機 master 掛了,這時 master 主機重啟後,因為 runid 發生了變化,所有的 slave 從機都要做一次全量複製。這將引起單節點和單機器的複製風暴,開銷會非常大。

解決方案:

可以採用樹狀結構降低多個從節點對主節點的消耗從節點採用樹狀樹非常有用,網路開銷交給位於中間層的從節點,而不必消耗頂層的主節點。但是這種樹狀結構也帶來了運維的複雜性,增加了手動和自動處理故障轉移的難度。

  • 單機器的複製風暴

由於 Redis 的單線程架構,通常單台機器會部署多個 Redis 實例。當一台機器(machine)上同時部署多個主節點(master)時,如果每個 master 主機只有一台 slave 從機,那麼當機器宕機以後,會產生大量全量複製。這種情況是非常危險的情況,帶寬馬上會被佔用,會導致不可用。

解決方案:

應該把主節點盡量分散在多台機器上,避免在單台機器上部署過多的主節點。當主節點所在機器故障後提供故障轉移機制,避免機器恢復後進行密集的全量複製

本次課程就講到這裡了,=====>下一節課就開始編寫命令測試運行今天的全量複製和部分複製以及延遲問題的實戰。

請大家關注我的專欄,更多的精彩等著你。謝謝。

推薦閱讀:

相关文章