階段是主從庫間建立連接、協(xié)商同步的過程,主要是為全量復(fù)制做準(zhǔn)備。在這一步,從庫和主庫建立起連接,并告訴主庫即將進(jìn)行同步,主庫確認(rèn)回復(fù)后,主從庫間就可以開始同步了。
具體來說,從庫給主庫發(fā)送 psync 命令,表示要進(jìn)行數(shù)據(jù)同步,主庫根據(jù)這個(gè)命令的參數(shù)來啟動(dòng)復(fù)制。psync 命令包含了主庫的 runID 和復(fù)制進(jìn)度 offset 兩個(gè)參數(shù)。runID,是每個(gè) Redis 實(shí)例啟動(dòng)時(shí)都會(huì)自動(dòng)生成的一個(gè)隨機(jī) ID,用來唯一標(biāo)記這個(gè)實(shí)例。當(dāng)從庫和主庫次復(fù)制時(shí),因?yàn)椴恢乐鲙斓?runID,所以將 runID 設(shè)為“?”。offset,此時(shí)設(shè)為 -1,表示次復(fù)制。主庫收到 psync 命令后,會(huì)用 FULLRESYNC 響應(yīng)命令帶上兩個(gè)參數(shù):主庫 runID 和主庫目前的復(fù)制進(jìn)度 offset,返回給從庫。從庫收到響應(yīng)后,會(huì)記錄下這兩個(gè)參數(shù)。這里有個(gè)地方需要注意,F(xiàn)ULLRESYNC 響應(yīng)表示次復(fù)制采用的全量復(fù)制,也就是說,主庫會(huì)把當(dāng)前所有的數(shù)據(jù)都復(fù)制給從庫。
第二階段,主庫將所有數(shù)據(jù)同步給從庫。從庫收到數(shù)據(jù)后,在本地完成數(shù)據(jù)加載。這個(gè)過程依賴于內(nèi)存快照生成的 RDB 文件。
具體來說,主庫執(zhí)行 bgsave 命令,生成 RDB 文件,接著將文件發(fā)給從庫。從庫接收到 RDB 文件后,會(huì)先清空當(dāng)前數(shù)據(jù)庫,然后加載 RDB 文件。這是因?yàn)閺膸煸谕ㄟ^ replicaof 命令開始和主庫同步前,可能保存了其他數(shù)據(jù)。為了避免之前數(shù)據(jù)的影響,從庫需要先把當(dāng)前數(shù)據(jù)庫清空。在主庫將數(shù)據(jù)同步給從庫的過程中,主庫不會(huì)被阻塞,仍然可以正常接收請(qǐng)求。否則,Redis 的服務(wù)就被中斷了。但是,這些請(qǐng)求中的寫操作并沒有記錄到剛剛生成的 RDB 文件中。為了保證主從庫的數(shù)據(jù)一致性,主庫會(huì)在內(nèi)存中用專門的 replication buffer,記錄 RDB 文件生成后收到的所有寫操作。
第三個(gè)階段,主庫會(huì)把第二階段執(zhí)行過程中新收到的寫命令,再發(fā)送給從庫。具體的操作是,當(dāng)主庫完成 RDB 文件發(fā)送后,就會(huì)把此時(shí) replication buffer 中的修改操作發(fā)給從庫,從庫再重新執(zhí)行這些操作。這樣一來,主從庫就實(shí)現(xiàn)同步了。