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