Redis原始碼剖析之主從複製

weixin_33912445發表於2018-11-12
13467292-0528dfa3e9323c03

2.資料庫狀態一致

主從複製,伺服器雙方資料庫將儲存相同的資料,這種現象稱為“資料庫狀態一致”

3.執行方式

>>>slaveof 127.0.0.1 6379

4.舊版複製功能的實現(2.8以前的版本)

複製功能都分為兩個基本步驟:同步和命令傳播

同步:將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態。

命令傳播:主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態不一致,讓主從伺服器資料庫重新回到一致狀態。

1.同步

當客戶端向從伺服器傳送slaveof命令,要求從伺服器複製主伺服器時,從伺服器首先需要執行同步操作,也就是將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態。而從伺服器對主伺服器的同步操作需要通過向主伺服器傳送SYNC命令來完成。

從伺服器傳送SYNC命令的執行步驟:

a.從伺服器向主伺服器傳送SYNC命令。

b.收到SYNC命令的主伺服器執行BGSAVE命令,在後臺生成一個RDB檔案,並使用一個緩衝區記錄從現在開始執行的所有寫命令。

c.當主伺服器的BGSAVE命令執行完畢時,主伺服器會將BGSAVE命令生成的RDB檔案傳送給從伺服器,從伺服器接收接收並載入這個RBD檔案,將自己的資料庫狀態更新至主伺服器執行BGSAVE命令時的資料庫狀態。

d.主伺服器將記錄在緩衝區裡面的所有寫命令傳送給從伺服器,從伺服器執行這些寫命令,將自己的資料庫狀態更新至主伺服器當前所處的狀態。

13467292-cf56774057a11491

2.命令傳播

在執行完同步操作以後,如果客戶端又再次向主伺服器傳送寫命令,如果此時該命令沒有傳播到從伺服器,那麼主從伺服器的資料庫狀態必然會不一樣,因此,在執行完同步操作以後,還必須得執行命令傳播,用來傳播主伺服器接收到的新的命令請求。

為了讓主從伺服器再次回到一致狀態,主伺服器需要對從伺服器執行命令傳播操作:主伺服器會將自己執行的那條寫命令,傳送給從伺服器,當從伺服器執行了相同的寫命令之後,主從伺服器將再次回到一致狀態。

3.舊版複製存在的缺陷

從伺服器對主伺服器的複製分為以下兩種:

初次複製:從伺服器沒有複製任何主伺服器,或者從伺服器當前要複製的主伺服器和上一次複製的主伺服器不同。

斷線後重複製: 處理命令傳播階段的主從伺服器因為網路原因而中斷了複製,但從伺服器通過自動重連線重新連線上主伺服器,並繼續複製主伺服器 。

13467292-44270393b782a0d6

當主從伺服器斷開以後,從伺服器通過自動重連連上主伺服器,然後從伺服器向主伺服器傳送SYNC命令,進行同步操作,但是主伺服器此時會將資料庫狀態寫入到RDB檔案中,如上述紅色方框(重複複製了許多鍵值對),這部分就是舊版複製存在的缺陷。

4.舊版複製問題的解決方案

為了解決舊版複製功能在處理斷線重複複製情況時的低效問題,redis從2.8以後,使用PSYNC命令代替SYNC命令來執行復制時的同步操作。

psync命令具有完整重同步和部分重同步兩種模式。

完整重同步:用以解決初次複製的問題。執行操作與sync一模一樣。

部分重同步:用於處理斷線後重複製情況:當從伺服器在斷線後重新連上主伺服器時,如果條件允許,主伺服器可以將主從伺服器連線斷開期間執行的寫命令傳送給從伺服器,從伺服器只要接收並執行這些寫命令,就可以將資料更新至主伺服器當前所處的狀態。

PSYNC命令的部分重同步解決了舊版複製功能在處理斷線後重複復制時出現的低效情況。

13467292-078e53dd0c3cb7ca

主從伺服器執行部分重同步的過程:

13467292-856112ce5fcb62a8

5.部分重同步的實現

要實現部分重同步,必須解決以下三個問題:

1.當前主從伺服器各複製了多少資料??

2.如果主從伺服器斷線以後,主伺服器新接收到的命令請求,該如何處理?

3.如果在一個叢集系統中,如何找到上一次複製的那個主伺服器呢?

部分重同步功能由以下三個部分構成:

a.主伺服器的複製偏移量和從伺服器的複製偏移量

b.主伺服器的複製積壓緩衝區

c.伺服器的執行ID

typedef struct redisClient {

// 複製狀態

int replstate; /* replication state if this is a slave */

// 用於儲存主伺服器傳來的 RDB 檔案的檔案描述符

int repldbfd; /* replication DB file descriptor */

// 讀取主伺服器傳來的 RDB 檔案的偏移量

off_t repldboff; /* replication DB file offset */

// 主伺服器傳來的 RDB 檔案的大小

off_t repldbsize; /* replication DB file size */

sds replpreamble; /* replication DB preamble. */

// 主伺服器的複製偏移量

long long reploff; /* replication offset if this is our master */

// 從伺服器最後一次傳送 REPLCONF ACK 時的偏移量

long long repl_ack_off; /* replication ack offset, if this is a slave */

// 從伺服器最後一次傳送 REPLCONF ACK 的時間

long long repl_ack_time;/* replication ack time, if this is a slave */

// 主伺服器的 master run ID

// 儲存在客戶端,用於執行部分重同步

char replrunid[REDIS_RUN_ID_SIZE+1]; /* master run id if this is a master */

// 從伺服器的監聽埠號

int slave_listening_port; /* As configured with: SLAVECONF listening-port */

// 最後被寫入的全域性複製偏移量

long long woff; /* Last write global replication offset. */

} redisClient;

下面我們對上面三個部分一一解釋一下:

複製偏移量

執行復制的雙方---主從伺服器都會維護一個複製偏移量。

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

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

通過對比主從伺服器的複製偏移量,程式很容易知道主從伺服器是否處於一致狀態。

主從狀態一致:

13467292-d1cca0a45eca2410

主從狀態不一致:

13467292-33aa99c07a98b02c

假如從伺服器A在斷線後就立即重新連線主伺服器,並且成功,那麼接下來,從伺服器將向主伺服器傳送PSYNC命令,報告從伺服器A當前的複製偏移量為10086,那麼這時,主伺服器應該對從伺服器執行完全重同步還是部分重同步?如果執行部分重同步的話,主伺服器又如何補償從伺服器A在斷線期間丟失的那部分資料呢?

複製積壓區

複製積壓區是由主伺服器維護的一個固定長度的佇列,預設大小為1M。

當主伺服器進行命令傳播時,它不僅將寫命令傳送給所有從伺服器,還會將寫命令入列到複製積壓區緩衝區裡面。如下圖:

13467292-4273707a9105183b

因此,主伺服器的複製積壓區裡面會儲存著一部分最近傳播的寫命令,並且複製積壓緩衝區會為佇列中的每個位元組記錄相應的複製偏移量。

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

如果offset偏移量之後的資料,仍然存在於複製積壓區裡面,那麼主伺服器將對從伺服器執行部分重同步操作。

如果offset偏移量之後的資料,不在複製積壓區裡面,那麼主伺服器將會對從伺服器進行完全重同步操作。

伺服器允許ID

每個Redis伺服器,不論是主伺服器還是從伺服器都會有自己的執行ID。這個ID在伺服器啟動時自動生成,由40個隨機十六進位制字元組成。

當從伺服器對主伺服器進行初次複製時,主伺服器會將自己的執行ID傳送給從伺服器,而從伺服器會將這個執行ID儲存起來。

當從伺服器斷線並重連上一個主伺服器時,從伺服器將向當前連線的主伺服器傳送自己的之前儲存的執行ID:

如果ID一致,說明短線後重連的就是之前連線的伺服器;

如果ID不一致,說明簡訊��重連的不是之前連結的伺服器,那麼主伺服器將對從伺服器進行完整重同步操作。

相關文章