1. 程式人生 > >redis 系列21 複製Replication (上)

redis 系列21 複製Replication (上)

原文: redis 系列21 複製Replication (上)

一.   概述

  使用和配置主從複製非常簡單,每次當 slave 和 master 之間的連線斷開時, slave 會自動重連到 master 上,並且無論這期間 master 發生了什麼, slave 都將嘗試讓自身成為 master 的精確副本。這個系統的執行依靠三個主要的機制:

  (1) 當一個 master 例項和一個 slave 例項連線正常時, master 會發送一連串的命令流來保持對 slave 的更新,以便於將自身(master)資料集的改變複製給 slave 。包括客戶端的寫入、key 的過期或master庫的其它改變動作。

  (2) 在Redis2.6之前,主從斷開重連後,會進行一次快照操作(rdb)然後將快照發送給從資料庫,即使斷開期間只有幾條命令被執行,這就使得斷開重聯後的資料恢復過程效率很低。Redis2.8之後,當 master 和 slave 之間的連線斷開之後(因為網路問題、主庫或從庫檢測到超時), slave 會重新連線上 master 並嘗試進行"部分重同步"也叫增量複製(partial resynchronization),用於獲取在斷開連線期間內丟失的命令流。

  (3) 當無法進行部分重同步時,slave 會請求進行"完整同步"(full resynchronization)。這會涉及到一個更復雜的過程,例如 master 需要建立所有資料的快照,將之傳送給 slave ,之後在master資料集更改時持續傳送命令流到 slave。也就是2.8後有了全部同步和部分重同步兩種模式,而2.6之前只有全部同步模式。

  

二. 複製特點

  Redis使用預設的非同步複製,具有低延遲和高效能,是絕大多數Redis使用的複製模式。從庫會非同步的確認與主庫定期接收到的資料量。主庫不會每次去等待從庫處理完命令。類似mysql半同步複製功能可以通過min-slaves配置選項來輔助。下面介紹Redis複製的一些特點:

  (1) Redis使用非同步複製,slave 和 master 之間非同步地確認處理的資料量。

  (2) 一個 master 可以擁有多個 slave。

  (3) slave 可以接受其他 slave 的連線。除了多個 slave 可以連線到同一個 master 之外, slave 之間也可以像層疊狀的結構(cascading-like structure)連線到其他 slave 。自Redis 4.0 版本起所有的 sub-slave 將會從 master 收到完全一樣的複製流。

  (4) Redis複製在 master這邊是非阻塞的。這意味著 master 在一個或多個 slave 進行初次同步或者是部分重同步時,可以繼續處理查詢請求。

  (5) 複製在 slave 這邊大部分也是非阻塞的。當 slave 進行初次同步時,它可以使用舊資料集處理查詢請求,假設你在redis.conf中配置了讓Redis這樣做的話。

  (6) 複製既可以被用在可伸縮性,以便只讀查詢可以有多個 slave 進行(例如 O(N) 複雜度的慢操作可以被下放到 slave ),或者僅用於資料安全。

  (7) 可以使用複製來避免 master 將全部資料集寫入磁碟造成的開銷:一種典型的技術是配置你的 master Redis.conf以避免對磁碟進行持久化,然後連線一個 slave ,其配置為不定期儲存或是啟用 AOF。但是,這個設定必須小心處理,因為重新啟動的 master 程式將從一個空資料集開始:如果一個 slave 試圖與它同步,那麼這個 slave 也會被清空。

  (8) 在使用Redis複製功能時,強烈建議在 master 和在 slave 中啟用持久化。假如:持久化被關閉了,Redis主節點重啟後其資料集合為空。而其它slave從節點會複製主節點資料,在複製時會銷燬自身之前的資料副本。這樣下來複制構架的所有節點資料都會被清空。

 

三 Redis複製同步原理

  3.1 全部同步的實現

    當客戶端向從伺服器傳送slaveof命令,要求從伺服器複製主伺服器時,從伺服器首先需要執行同步操作,將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀,過程如下:

      (1) 從伺服器向主伺服器傳送psync命令。

      (2) 收到psync命令的主伺服器執行BGSAVE命令,開啟一個後臺子程序生成一個 RDB 快照檔案,並同時使用一個緩衝區記錄從現在執行的所有寫命令。

      (3) 當主伺服器的BGSAVE命令執行完,主伺服器會將生成的RDB檔案傳送給從伺服器,從伺服器接收並載入這個RDB檔案,然後載入檔案到記憶體。將自己的資料庫狀態更新至主伺服器執行BGSAVE命令時的資料庫狀態。

      (4) 主伺服器將記錄在緩衝區裡面的所有寫命令傳送給從伺服器,從伺服器執行這些寫命令,將自己的資料庫狀態更新至主伺服器資料庫當前所處狀態。這個過程以指令流的形式完成並且和Redis協議本身的格式相同。

      下面是使用psync命令進行全部同步的過程:

時間

主伺服器

從伺服器

T0

伺服器啟動

伺服器啟動

T1

執行set  k1  v1

 

T2

執行set  k2  v2

 

T4

..

向主伺服器傳送psync命令

T5

接收到從伺服器的psync命令,執行bgsave命令,建立RDB檔案,並使用緩衝區記錄接下來執行所有寫命令.

 

T6

執行set  k3  v3,將這個命令記錄在緩衝區中

 

T7

執行set  k4  v4,將這個命令記錄在緩衝區中

 

T8

Bgsave執行完成,向從伺服器傳送RDB檔案

 

T9

 

接收載入RDB檔案

T10

向從伺服器傳送緩衝區中儲存的寫命令

 

T11

 

接收緩衝區中的寫命令,執行

T12

同步完成

同步完成

  

  3.2 部分重同步的實現

    在主從伺服器斷開後,從伺服器會嘗試不斷的傳送psync命令,使用部分重同步模式,讓主從伺服器重新回到一致狀態。使用部分重同步主伺服器只需要將從伺服器缺少的寫命令傳送給從伺服器,從伺服器執行就可以了。功能由三個部份構成:

    (1) 主伺服器的複製偏移量(replication offset)和從伺服器的複製偏移量。

    (2)主伺服器的複製積壓緩衝區(replication backlog)。

    (3) 伺服器的執行ID (run ID) 。

    3.2.1 複製偏移量:主伺服器每次向從伺服器傳播N個位元組的資料時,就將自己的複製偏移量的值加上N。從伺服器每次接收到主伺服器傳播來的N個位元組的資料時,就將自己的複製偏移量的值加上N。

    例如:主伺服器向從伺服器傳播長度為33個位元組資料,那麼主伺服器的複製偏移量將更新為10086+33=10119,而從伺服器在接收到主伺服器傳播的資料之後,也會將複製偏移量更新為10119。如果主從偏移量相同,表示主從伺服器處於一致狀態,反之則不一致。

 

    3.2.2 複製積壓緩衝區: 該緩衝區是由主伺服器維護一個固定長度先進先出的佇列,預設大小為1MB。當主伺服器進行命傳播時,它不僅會將寫命令傳送給所有從伺服器,還會將寫命令入佇列到複製積壓緩衝區中。因此主伺服器的複製積壓緩衝區中會儲存著一部分最近傳播的寫命令,並且複製積壓緩衝區會為佇列中的每個位元組記錄相應的複製偏移量。

    當從伺服器重新連上主伺服器時,從伺服器會通過psync命令將自己的複製偏移量offset傳送給主伺服器,主伺服器會根據這個複製偏移量來決定從伺服器執行何種同步操作:

      (1) 如果從伺服器傳送的offest偏移量之後的資料仍然存在於複製積壓緩衝區中,那麼主伺服器將對從伺服器執行部分重同步操作。

      (2) 如果從伺服器傳送的如果offest偏移量之後的資料已經不存在於複製積壓緩衝區中,那麼主服器將對從伺服器執行完整同步操作。

      注意:緩衝區預設大小為1MB,如果主伺服器需要執行大量寫命令,又或者主從伺服器斷線後重連線所需的時間比較長,那麼這個大小也許並不合適。檢視積壓緩衝區大小(單位位元組)如下:

        127.0.0.1:6379> config get repl-backlog-size
        1) "repl-backlog-size"
        2) "1048576"

    

    3.2.3  伺服器執行ID:每個redis伺服器,不論主從都有自己的執行ID。執行ID在伺服器啟動時自動生成,由40個隨機的十六進位制字元組成。當從伺服器對主伺服器進行初次複製時,主伺服器會將自已執行的ID傳送給從伺服器,從伺服器會將這個執行ID儲存起來。當從伺服器斷開重連到一個主伺服器時,從伺服器將向當前連線的主伺服器傳送之前儲存的執行ID

      (1) 如果從伺服器儲存的執行ID和當前連線的主伺服器的執行ID相同,那麼說明從伺服器斷開後重連的還是之前的主伺服器,主伺服器將繼續嘗試執行部分重同步操作。

      (2) 反之,如果執行ID不相同,那說明從伺服器斷開後重連的不是之前的主伺服器,主伺服器將對從伺服器執行完整同步操作。

      下面使用psync命令來進行斷線後,重複制的過程:

時間

主伺服器

從伺服器

T0

主從伺服器完成同步

主從伺服器完成同步

T1

執行並傳播set k1 v1

執行主伺服器傳來的set k1 v1

..

..

..

T10087

主從伺服器連線斷開

主從伺服器連線斷開

T10088

執行set  k10087 v10087

斷線後,嘗試重新連線主伺服器

T10089

執行set  k10088 v10088

斷線後,嘗試重新連線主伺服器

T10090

主從伺服器重新連線

主從伺服器重新連線

T10091

 

向主伺服器傳送psync命令

T10092

向從伺服器返回+continue回覆,表示執行部分重同步

 

T10093

 

接收+continue回覆,表示執行部分重同步

T10094

主從伺服器再次完成同步

主從伺服器再次完成同步

 

四. psync命令的實現

  4.1 PSYNC命令的呼叫方法有兩種:

    (1) 如果從伺服器以前沒有複製過任何主伺服器,那麼從伺服器在開始一次新的複製時,將向主伺服器傳送psync ? -1 命令,主動請求主伺服器進行完整同步。

    (2) 相反,如果從伺服器已經複製過某個主伺服器,那麼從伺服器在開始一次新的複製時,將向主伺服器傳送 psync runid offset 命令。通過兩個引數來判斷應該對伺服器執行哪種同步操作。

  4.2 根據情況,主伺服器接收到psync命令返回以下三種回覆的其中一種:

    (1) 如果主伺服器返回 +fullresync  runid  offset回覆,那麼表示主伺服器將與從伺服器執行“完整同步”操作。

    (2) 如果主伺服器返回+continue回覆,那麼表示主伺服器將與從伺服器執行“部分重同步”操作。

    (3) 如果主伺服器返回-err回覆,那麼表示主伺服器的版本低於redis 2.8,它識別不了psync命令,從伺服器將向主伺服器傳送sync命令,並與主伺服器執行完整同步操作。

    下圖是psync命令執行完整同步和部分重同步時可能遇到的情況: