1. 程式人生 > >《Redis設計與實現》閱讀筆記10-複製

《Redis設計與實現》閱讀筆記10-複製

14 複製

在redis中使用slaveof命令,可以使得伺服器去複製另一個伺服器,成為那個伺服器的從伺服器,稱為主從複製

14.1 舊版複製功能

舊版複製的實現由兩種方式組成

  • 同步:通過傳送主伺服器的rdb檔案給從伺服器與緩衝區儲存後續寫命令使得主從伺服器狀態一致
  • 命令傳播:主從伺服器狀態一致後,主伺服器傳送後續修改資料庫的命令給從伺服器保證主從伺服器狀態一致

14.1.1 同步

當一個伺服器使用slaveof命令成為另一個伺服器的從伺服器時,使用SYNC命令啟動同步操作,過程如下

  • 從伺服器傳送SYNC命令給主伺服器
  • 主伺服器執行BGSAVE命令生成rdb檔案,並使用一個緩衝區儲存後續寫命令
  • 從伺服器使用主伺服器傳送過來的rdb檔案同步到和主伺服器狀態一致
  • 主服務將緩衝區中的寫命令傳送給從伺服器執行,使得其狀態完全一致

14.1.2 命令傳播

為了保證主從伺服器狀態一致是一致的,使用同步命令初始化後,後續的持續性的一致性需要命令傳播來保證。
命令傳播是指在主伺服器執行寫命令時也同時會把這個命令傳送給從伺服器,讓從伺服器再執行一遍。

14.2 舊版複製功能的缺陷

在主從伺服器正常連線後,若突然出現主從伺服器的斷開(非正常斷開),在重新連線以後,在斷開時間段中由於命令傳播功能無法正常執行,導致主從伺服器的資料庫狀態存在差異,此時需要重寫進行同步操作。

此時的同步操作會同步所有資料,但其實主從伺服器之間只差了斷開期間的幾條指令。之前的大部分資料完全一樣,而再次執行同步操作就顯得價效比極低。,並且SYNC命令是一個非常耗費資源的操作{包括一次rdb檔案生成(消耗cpu,記憶體,磁碟I/O資源),rdb檔案的傳送(頻寬與流量),從伺服器載入RDB檔案(從伺服器阻塞)}

14.3 新版複製的實現

新版複製(redis2.8版本以後)使用PSYNC命令代替SYNC命令來執行復制時的同步操作,PSYNC命令具有完整全同步和部分重同步兩種模式

  • 完整全同步和SYNC命令的同步一樣,會使用rdb檔案全部複製主伺服器的所有資料
  • 部分重同步處理舊版複製的缺陷,利用複製偏移量,複製積壓緩衝區,伺服器執行ID實現只複製需要部分的功能,而不會因為缺少部分資料,而複製所有資料

14.4 部分重同步的實現

部分重同步利用複製偏移量,複製積壓緩衝區,伺服器執行ID實現只複製需要部分的功能

14.4.1 複製偏移量

主伺服器和從伺服器都維護了一個複製偏移量,主伺服器執行寫操作,寫N個位元組資料,就讓主伺服器的複製偏移量增加N,每次主伺服器向從伺服器執行命令傳播時,傳播N個位元組資料,就讓從伺服器的複製偏移量增加N。

若主從伺服器狀態一致,那他們的複製偏移量總是一樣。

14.4.2 複製積壓緩衝區

複製積壓區是一個固定長度的迴圈佇列,內部儲存的資料結構,每個都儲存著偏移量與該偏移量對應一個位元組的寫命令內容

偏移量 187 188 189 190 191 192 193 194 195 196 197
位元組值 ‘*’ 3 ‘\r’ ‘\n’ ‘$’ 3 ‘\r’ ‘\n’ ‘S’ ‘E’ ‘T’

當從伺服器重新連線以後,會發送自己儲存的主伺服器執行ID與自己的複製偏移量傳送給主伺服器,若主伺服器執行ID與從伺服器傳送的斷線前的主服務執行ID一致,且從伺服器的複製偏移量在複製積壓緩衝區中未被沖走(從伺服器偏移量+1還在迴圈佇列中有儲存),就會根據偏移量差,在複製積壓緩衝區中取命令,並執行,然後更新從伺服器的複製偏移量,使得主從伺服器狀態一致。

關於快取區大小的選擇:
緩衝區的大小一般根據,伺服器重連平均時間與主伺服器平均每秒執行寫命令數量來估算,選擇的大小一般都需要大於這兩者的乘積,來保證大多數從伺服器重連以後是執行部分重同步,達到節約資源的目的

14.4.3 伺服器執行ID

不管是從伺服器還是主伺服器都有自己的執行ID(四十個隨機的16進位制字元),從伺服器會儲存當前連線的主伺服器的執行ID,儲存的主伺服器執行ID用於從伺服器使用PSYNC命令時,若從伺服器儲存的有之前的主伺服器執行ID,會把這個執行ID一起發給主伺服器,若主伺服器發現這個ID和自己的執行ID一樣,就會考慮複製偏移量在緩衝區中是否有儲存,來決定執行部分重同步還是完整重同步,若執行ID不一樣,預設使用完整重同步。

14.5 PSYNC命令的實現

現在,來完整了解PSYNC的實現流程

  • 從伺服器請求複製
    • 如果一個伺服器成為從伺服器時,沒有儲存主伺服器執行ID(第一次成為主伺服器或者執行過SLAVEOF no one命令),那麼伺服器向主伺服器傳送PSYNC ? -1,主動請求完整重同步
    • 如果一個伺服器成為從伺服器時,儲存的有主伺服器執行ID,那麼傳送PSYNC <runid> <offset>將之前連線的主伺服器執行ID與自己的複製偏移量傳送給主伺服器。
  • 主伺服器回覆
    • 若主伺服器回覆+FULLRESYNC 表示將進行完整重同步,主伺服器傳送的兩個引數是自己的執行ID和複製偏移量,從伺服器需要儲存新的主伺服器執行ID和更新複製偏移量
    • 若主伺服器回覆+CONTINUE,表示將進行部分重同步
    • 若回覆-ERR,表示出錯,主伺服器版本低於Redis2.8,不能識別PSYNC命令,接下來會發送SYNC命令,進行完整重同步

14.6 複製的實現

使用SLAVEOF <master-ip> <master-port>可以讓從伺服器去複製一個主伺服器,引數為主伺服器的地址和埠

14.6.1 設定主伺服器的地址和埠

從伺服器將主伺服器的地址和埠儲存在自己的結構中(redisServer的masterhost屬性與masterport屬性用於儲存主伺服器的地址和埠)

14.6.2 建立套接字連線

設定主伺服器的地址和埠後,從伺服器會為主伺服器建立一個套接字並關聯一個檔案事件處理器來處理複製工作(接收RDB檔案和主伺服器傳送的),主伺服器在接受從伺服器套接字連線後,將這個套接字當做客戶端處理,併為其建立相應的客戶端狀態。

從伺服器會向主伺服器傳送命令請求,也會向伺服器傳送命令請求,所以此時的從伺服器同時具有伺服器與客戶端兩個狀態。

14.6.3 傳送PING命令

從伺服器成為主伺服器的客戶端後,第一件事是給主伺服器傳送PING命令

  • 作用一是檢測套接字的讀寫狀態是否正常,通訊是否正常進行
  • 作用二檢查主伺服器能否正常處理命令請求

伺服器接收到PING命令後回覆有三種情況

  • 若從伺服器不能在規定時間內讀取命令回覆的內容,說明主從伺服器的網路連線不穩定,此時會斷開並重新建立連線主伺服器的套接字
  • 返回一個錯誤,表示主伺服器暫時沒辦法處理從伺服器的命令請求,此時會斷開並重新建立連線主伺服器的套接字
  • 返回PONG,表示正常連線,繼續執行後續複製工作

14.6.4 身份驗證

主伺服器給從伺服器返回PONG以後,若從伺服器設定了masterauth選項,那麼就會進行身份驗證。

身份驗證時,從伺服器會給主伺服器傳送命令AUTH <masterauth>,命令會帶上masterauth選項對應的值。

身份驗證階段會發生以下情況

  • 若主伺服器沒有設定requirepass選項,從伺服器沒有設定masterauth選項,沒有驗證,複製正常進行
  • 若身份驗證傳送命令中masterauth選項對應的值與主伺服器requirepass選項對應的值相同,驗證通過,複製工作繼續。
  • 若身份驗證傳送命令中masterauth選項對應的值與主伺服器requirepass選項對應的值不相同,主伺服器返回一個invalid password錯誤
  • 若主伺服器設定了requirepass選項,但從伺服器沒有設定masterauth選項,返回NOAUTH錯誤
  • 若主伺服器沒有設定requirepass選項,但從伺服器設定了masterauth選項,返回no password is set錯誤

所有錯誤都會中止複製工作,並從建立套接字開始重新開始複製工作,知道驗證通過,或從伺服器放棄複製

14.6.5 傳送埠資訊

身份驗證以後,從伺服器使用REPLICONF listening-port ,向主伺服器傳送從伺服器的監聽埠號。

主伺服器接收到以後,會把從伺服器的埠號儲存到客戶端狀態的slave_listening_port屬性中,這個屬性目前唯一的作用是用於執行INFO replication命令時顯示當前伺服器的從伺服器對應客戶端狀態中列印從伺服器的埠號。

14.6.6同步

前面的五步工作完成後,就開始同步操作,根據情況(前面提及的同步操作)選擇執行完整重同步或者部分重同步。

14.6.7 命令傳播

長期維持主從伺服器的狀態一致,就需要命令傳播,主伺服器將寫命令傳送給從伺服器執行。

14.7 心跳檢測

命令傳播階段,從伺服器預設已每秒一次的頻率,向主伺服器傳送REPLCONF ACK <replication_offset>命令,replication_offset是從伺服器的複製偏移量。
作用:

  • 檢測網路連線是否正常
  • 輔助實現min-slaves選項
  • 檢測命令丟失

14.7.1 檢測網路連線狀態

以固定頻率傳送命令來檢測網路連線是否正常,主伺服器若在間隔一秒以上沒有收到REPLCONF ACK <replication_offset>命令,就知道網路連接出問題,另外在使用INFO replication命令時,lag屬性中可以檢視上一次執行REPLCONF ACK <replication_offset>命令距離現在的時間。這個值一般為0或1,若為其他值說明有問題。

14.7.2 輔助實現min-slaves選項

Redis的min-slaves-to-write和min-slaves-max-lag兩個選項可以防止主伺服器在不安全的情況下執行寫命令

min-slaves-to-write 3
min-slaves-max-lag 10

那麼在從伺服器的數量少於3個,或者三個從伺服器的延遲(lag)值都大於或等於10秒時,主伺服器將拒絕執行寫命令,這裡的延遲值就是上面提到的INFO replication命令的lag值。

14.7.3 檢測命令丟失

我們從命令:REPLCONF ACK <replication_offset>就可以知道,每傳送一次這個命令從伺服器都會向主伺服器報告一次自己的複製偏移量。那此時儘管主伺服器傳送給從伺服器的SET key value丟失了。則主從伺服器的複製偏移量就會不一樣,主伺服器馬上就知道了。併到複製緩衝區去尋找對應丟失的命令。