Redis之主從服務(wù)器的復(fù)制

本文主要介紹Redis集群中主從服務(wù)器復(fù)制功能的實(shí)現(xiàn)。

在Redis中臊恋,用戶可以通過(guò)執(zhí)行SLAVEOF命令或設(shè)置slaveof選項(xiàng)琅催,讓一個(gè)服務(wù)器去復(fù)制另一個(gè)服務(wù)器,稱被復(fù)制的服務(wù)器為主服務(wù)器(master)界逛,而對(duì)主服務(wù)器進(jìn)行復(fù)制的服務(wù)器稱為從服務(wù)器(slave)

例如:

127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK

則服務(wù)器127.0.0.1:12345就成為了127.0.0.1 6379的slave纺座,127.0.0.1 6379成為了127.0.0.1:12345的master息拜。

進(jìn)行復(fù)制中的主從服務(wù)器雙方的數(shù)據(jù)庫(kù)保存同樣的數(shù)據(jù),概念上稱之為數(shù)據(jù)庫(kù)的一致性净响。

I少欺、舊版復(fù)制功能的實(shí)現(xiàn)

Redis的復(fù)制功能分為同步(sync)命令傳播(command propagate)兩個(gè)操作:

· 同步操作用于將從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器當(dāng)前所處的數(shù)據(jù)庫(kù)狀態(tài)。
· 命令傳播操作則用于主服務(wù)器的數(shù)據(jù)被修改后馋贤,讓主從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)重新回到一致赞别。

1.1 同步

從服務(wù)器對(duì)主服務(wù)器的同步操作需要通過(guò)向主服務(wù)器發(fā)送SYNC命令來(lái)完成,以下是SYNC命令的執(zhí)行步驟:
1配乓、 從服務(wù)器向主服務(wù)器發(fā)送SYNC命令仿滔。
2、收到命令之后的主服務(wù)器執(zhí)行BDSAVE命令犹芹,在后臺(tái)生成一個(gè)RDB文件崎页,并使用一個(gè)緩沖區(qū)記錄從現(xiàn)在開(kāi)始執(zhí)行的所有寫(xiě)命令。
3腰埂、當(dāng)主服務(wù)器BGSAVE命令執(zhí)行完畢時(shí)飒焦,主服務(wù)器會(huì)將生成的RDB文件發(fā)送給從服務(wù)器,從服務(wù)器load這個(gè)RDB文件屿笼,將自己的數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器執(zhí)行BGSAVE命令時(shí)數(shù)據(jù)庫(kù)狀態(tài)牺荠。
4翁巍、主服務(wù)器將記錄在緩沖區(qū)中的所有寫(xiě)命令發(fā)送給從服務(wù)器,從服務(wù)器執(zhí)行這些寫(xiě)命令志电,將自己的數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器當(dāng)前所處狀態(tài)。

下圖展示了這一過(guò)程:

下圖說(shuō)明了一個(gè)同步實(shí)例

1.2 命令傳播

主服務(wù)器每次執(zhí)行寫(xiě)名列會(huì)同時(shí)將命令發(fā)送給從服務(wù)器執(zhí)行蛔趴,從而實(shí)現(xiàn)命令傳播挑辆,達(dá)到主從服務(wù)器的一致性。

II孝情、舊版復(fù)制功能的缺點(diǎn)

從服務(wù)器在除了在初次復(fù)制主服務(wù)器之外鱼蝉,還會(huì)在斷線后重復(fù)制是也進(jìn)行賦值操作。而剛剛介紹的舊版復(fù)制功能對(duì)于在斷線后重復(fù)制情況來(lái)說(shuō)箫荡,效率很低魁亦。

這是由于需要重寫(xiě)執(zhí)行SYNC操作引起的(一旦執(zhí)行就會(huì)發(fā)生RDB的復(fù)制等操作)。
如下圖所示:

而要解決這個(gè)問(wèn)題的關(guān)鍵就在于上圖中紅圈位置的復(fù)制操作羔挡,我們應(yīng)該盡量不再重新使用SYNC命令洁奈,而采用其他方式。

III绞灼、新版復(fù)制功能的實(shí)現(xiàn)

在Redis2.8版本之后利术,采用了PSYNC命令的代替了SYNC同步操作。

PSYNC命令具有完整重同步部分重同步兩種模式:

· 完整重同步與舊版本的SYNC命令一樣低矮。
· 部分重同步專門(mén)用于處理斷線后重復(fù)制的情況: 當(dāng)從服務(wù)器在斷線后重新連接主服務(wù)器時(shí)印叁,如果條件允許,主服務(wù)器可以將主從服務(wù)器連接斷開(kāi)期間執(zhí)行的寫(xiě)命令發(fā)送給從服務(wù)器军掂,從服務(wù)器只要接收并執(zhí)行這些寫(xiě)命令轮蜕,就可以將數(shù)據(jù)塊更新至最新?tīng)顟B(tài)。

下圖展示了相同情況下使用部分重同步的實(shí)例:

IV蝗锥、部分重同步的實(shí)現(xiàn)

部分重同步功能由以下三個(gè)部分構(gòu)成:
· 主服務(wù)器的復(fù)制偏移量和從服務(wù)器的復(fù)制偏移量跃洛;
· 主服務(wù)器的復(fù)制積壓緩沖區(qū)
· 服務(wù)器的運(yùn)行ID终议。

4.1 復(fù)制偏移量

執(zhí)行復(fù)制的雙方都要維護(hù)一個(gè)復(fù)制偏移量
· 主服務(wù)器每次向從服務(wù)器傳播N個(gè)字節(jié)的數(shù)據(jù)時(shí)税课,就將自己的復(fù)制偏移量+N;
· 從服務(wù)器每次收到主服務(wù)器傳播來(lái)的N個(gè)字節(jié)的數(shù)據(jù)時(shí)痊剖,就將自己的復(fù)制偏移量+N韩玩;

復(fù)制偏移量具有兩個(gè)作用:

· 通過(guò)對(duì)比主從服務(wù)器的復(fù)制偏移量,程序可以很容易的知道主從服務(wù)器的數(shù)據(jù)是否處于一致?tīng)顟B(tài)陆馁;
· 通過(guò)對(duì)比主從服務(wù)器的復(fù)制偏移量找颓,可以知道從服務(wù)器丟失了哪些數(shù)據(jù),從而執(zhí)行部分重同步叮贩。

4.2 復(fù)制積壓緩沖區(qū)

復(fù)制積壓緩沖區(qū)是由主服務(wù)器維護(hù)的一個(gè)固定長(zhǎng)度的先進(jìn)先出隊(duì)列击狮。

當(dāng)服務(wù)器執(zhí)行命令傳播時(shí)佛析,不僅會(huì)將寫(xiě)命令傳播給從服務(wù)器,還會(huì)將寫(xiě)命令寫(xiě)入復(fù)制積壓緩沖區(qū)中:

當(dāng)從服務(wù)器重新連上主服務(wù)器時(shí)彪蓬,從服務(wù)器會(huì)通過(guò)PSYNC命令將自己的復(fù)制偏移量offset發(fā)送給主服務(wù)器寸莫,主服務(wù)器會(huì)根據(jù)這個(gè)復(fù)制偏移量來(lái)決定對(duì)從服務(wù)器執(zhí)行何種同步操作:
· 如果offset偏移量之后的數(shù)據(jù)仍然存在于復(fù)制積壓緩沖區(qū)中,則主服務(wù)器將對(duì)從服務(wù)器執(zhí)行部分重同步档冬;
· 如果offset偏移量之后的數(shù)據(jù)已經(jīng)不在復(fù)制積壓緩沖區(qū)中膘茎,主服務(wù)器會(huì)對(duì)從服務(wù)器執(zhí)行完整的重同步操作;

4.3 服務(wù)器運(yùn)行ID

每個(gè)Redis服務(wù)器酷誓,都會(huì)有自己的運(yùn)行ID披坏,實(shí)現(xiàn)部分重同步需要復(fù)制偏移量復(fù)制積壓緩沖區(qū)盐数,運(yùn)行ID一起完成棒拂。

當(dāng)從服務(wù)器對(duì)主服務(wù)器進(jìn)行初次復(fù)制時(shí),主服務(wù)器會(huì)將自己的運(yùn)行ID發(fā)送給從服務(wù)器玫氢,從服務(wù)器將主服務(wù)器的運(yùn)行ID保存起來(lái)帚屉。

當(dāng)從服務(wù)器斷線并重寫(xiě)連接上一個(gè)主服務(wù)器之后(這個(gè)主服務(wù)器可能已經(jīng)不是之前的主服務(wù)器,可以參見(jiàn)Redis中的sentinel)漾峡,從服務(wù)器向當(dāng)前連接的主服務(wù)器發(fā)送自己之前保存的運(yùn)行ID:
· 如果從服務(wù)器保存的運(yùn)行ID和當(dāng)前主服務(wù)器的運(yùn)行ID相同涮阔,則說(shuō)明從服務(wù)器斷線之前復(fù)制的就是當(dāng)前連接的這個(gè)主服務(wù)器,主服務(wù)器可以嘗試執(zhí)行部分重同步操作灰殴;
· 如果主服務(wù)器發(fā)現(xiàn)與自己的運(yùn)行ID不同敬特,則只能執(zhí)行完整重同步操作;

V牺陶、PSYNC命令的實(shí)現(xiàn)

PSYNC命令的調(diào)用方法有兩種:
1伟阔、 如果從服務(wù)器以前沒(méi)有復(fù)制過(guò)任何主服務(wù)器,或者之前執(zhí)行過(guò)SLAVEOF no one命令掰伸,則從服務(wù)器在開(kāi)始一次新的復(fù)制時(shí)將向主服務(wù)器發(fā)送PSYNC ? -1命令皱炉,主動(dòng)請(qǐng)求主服務(wù)器進(jìn)行完整重同步

2狮鸭、 如果從服務(wù)器已經(jīng)復(fù)制過(guò)某個(gè)主服務(wù)器合搅,則從服務(wù)器在開(kāi)始一次新的復(fù)制時(shí)會(huì)向主服務(wù)器發(fā)送PSYNC <runid> <offset>命令,runid為上一次保存的主服務(wù)器運(yùn)行ID歧蕉,offset為從服務(wù)器維護(hù)的復(fù)制偏移量灾部。

主服務(wù)器根據(jù)接收到的PSYNC命令,會(huì)執(zhí)行下面的三種回復(fù):
1惯退、 主服務(wù)器返回+FULLRESYNC <runid> <offset>回復(fù)赌髓,表示主服務(wù)器與從服務(wù)器執(zhí)行完整重同步,runid為主服務(wù)器自己的運(yùn)行ID,offset為主服務(wù)器自身的復(fù)制偏移量锁蠕,從服務(wù)器將用這個(gè)值初始化自己的偏移量夷野。
2、 主服務(wù)器回復(fù)+COUTINUE荣倾,表示主服務(wù)器將與從服務(wù)器執(zhí)行部分重同步悯搔,根據(jù)接收到的offset,已經(jīng)主服務(wù)器自己的復(fù)制積壓緩沖區(qū)來(lái)完成舌仍。
3妒貌、 主服務(wù)器回復(fù)-ERR,表示主服務(wù)器版本低于Redis2.8抡笼,無(wú)法識(shí)別PSYNC命令苏揣,之后從服務(wù)器將發(fā)送SYNC黄鳍,執(zhí)行低版本的完整重同步推姻。

下圖完整描述了這一過(guò)程:

VI、復(fù)制的實(shí)現(xiàn)

6.1 步驟1:設(shè)置主服務(wù)器的地址和端口

客戶端向從服務(wù)器發(fā)送:

127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK

從服務(wù)器首先要做的是將客戶端給定的主服務(wù)器IP地址127.0.0.1以及端口6379保存到服務(wù)器狀態(tài)的masterhost和masterport屬性中:

struct redisServer {
    //主服務(wù)器地址
    char *masterhost;
    //端口
    int masterport;
};
6.2 步驟2: 建立套接字連接

從服務(wù)器根據(jù)命令的IP地址和端口框沟,創(chuàng)建向主服務(wù)器的套接字連接:

創(chuàng)建套接字成功藏古,則從服務(wù)器會(huì)專門(mén)為這個(gè)套接字關(guān)聯(lián)一個(gè)用于復(fù)制工作的文件事件處理器,負(fù)責(zé)執(zhí)行后續(xù)的復(fù)制工作忍燥。

主服務(wù)器在accept從服務(wù)器的套接字連接之后拧晕,將為該套接字創(chuàng)建相應(yīng)的客戶端狀態(tài),并將從服務(wù)器看作是一個(gè)連接到主服務(wù)器的客戶端來(lái)對(duì)待梅垄,這時(shí)從服務(wù)器將同時(shí)具有服務(wù)器和客戶端兩個(gè)身份厂捞。
因?yàn)閺?fù)制工作接下來(lái)的幾個(gè)步驟都會(huì)以從服務(wù)器向主服務(wù)器發(fā)送命令請(qǐng)求的形式進(jìn)行,所以理解從服務(wù)器是主服務(wù)器的客戶端相當(dāng)重要队丝。

6.3 步驟3: 發(fā)送PING命令

從服務(wù)器成為主服務(wù)器的客戶端之后靡馁,向主服務(wù)器發(fā)送PING命令:

這個(gè)PING命令有兩個(gè)作用:
1、檢查套接字的讀寫(xiě)狀態(tài)是否正常机久;
2臭墨、檢查主服務(wù)器是否能正常處理請(qǐng)求。

而從服務(wù)器會(huì)根據(jù)主服務(wù)器的相應(yīng)回復(fù)狀態(tài)判斷下一步的動(dòng)作:
1膘盖、如果主服務(wù)器向從服務(wù)器回復(fù)一個(gè)命令胧弛,但從服務(wù)器在規(guī)定時(shí)間內(nèi)無(wú)法讀取出命令回復(fù)的內(nèi)容,則表示主從服務(wù)器之間的網(wǎng)絡(luò)連接狀態(tài)不佳侠畔。
從服務(wù)器會(huì)斷開(kāi)并重新創(chuàng)建連向主服務(wù)器的套接字结缚;
2、如果主服務(wù)器返回了一個(gè)錯(cuò)誤软棺,表示主服務(wù)器暫時(shí)無(wú)法處理從服務(wù)器的命令請(qǐng)求掺冠,從服務(wù)器同樣斷開(kāi)并重新創(chuàng)建連向主服務(wù)器的套接字;
3、從服務(wù)器讀取到了PONG命令德崭,表示主從服務(wù)器網(wǎng)絡(luò)連接正常斥黑,繼續(xù)執(zhí)行下面的任務(wù)。

6.4 步驟4: 身份驗(yàn)證

身份驗(yàn)證步驟可以通過(guò)下圖來(lái)說(shuō)明:

6.5 步驟5: 發(fā)送端口信息

在執(zhí)行完身份驗(yàn)證步驟之后眉厨,從服務(wù)器會(huì)向主服務(wù)器發(fā)送從服務(wù)器的監(jiān)聽(tīng)端口號(hào)(用于后續(xù)的命令傳播等操作)锌奴,主服務(wù)器將端口號(hào)記錄在從服務(wù)器所對(duì)應(yīng)的客戶端狀態(tài)中:

typedef struct redisClient {
    //從服務(wù)器的監(jiān)聽(tīng)端口號(hào)
    int slave_listening_port;

} redisClient;
6.6 步驟6: 同步

從服務(wù)器向主服務(wù)器發(fā)送PSYNC命令,執(zhí)行同步操作憾股,并將自己的數(shù)據(jù)庫(kù)更新至主服務(wù)器數(shù)據(jù)庫(kù)當(dāng)前狀態(tài)鹿蜀。

在同步操作執(zhí)行之前,只有從服務(wù)器是主服務(wù)器的客戶端服球,但在執(zhí)行同步操作之后茴恰,主服務(wù)器也會(huì)成為從服務(wù)器的客戶端
· 如果要執(zhí)行完整重同步操作,主服務(wù)器需要成為從服務(wù)器的客戶端斩熊,才能將保存在緩沖區(qū)中的寫(xiě)命令發(fā)送給從服務(wù)器執(zhí)行往枣;
· 如果執(zhí)行的部分重同步操作,主服務(wù)器需要成為從服務(wù)器客戶端粉渠,才能將復(fù)制積壓緩沖區(qū)的內(nèi)容發(fā)送給從服務(wù)器執(zhí)行分冈。

因此,在同步操作執(zhí)行之后霸株,主從服務(wù)器都是雙方的客戶端雕沉,它們可以互相向?qū)Ψ桨l(fā)送命令請(qǐng)求,或者互相向?qū)Ψ椒祷孛罨貜?fù):

6.7 步驟7: 命令傳播

主服務(wù)器將自己的寫(xiě)命令發(fā)送給從服務(wù)器執(zhí)行去件,進(jìn)入命令傳播階段坡椒,這一操作的基礎(chǔ)也是主服務(wù)器時(shí)從服務(wù)器的客戶端

VII尤溜、心跳檢測(cè)

在命令傳播階段倔叼,從服務(wù)器會(huì)以每秒一次的頻率向主服務(wù)器發(fā)送心跳檢測(cè)命令:
REPLCONF ACK <replication_offset>
replication_offset是從服務(wù)器當(dāng)前的復(fù)制偏移量。

REPLCONF ACK命令對(duì)主從服務(wù)器有三個(gè)作用:
· 檢測(cè)網(wǎng)絡(luò)連接狀態(tài)靴跛;
· 輔助實(shí)現(xiàn)min-slaves選項(xiàng)(min-slaves表示最小從服務(wù)器數(shù)量缀雳,可以防止主服務(wù)器在不安全的狀態(tài)執(zhí)行寫(xiě)命令,不安全的狀態(tài)指從服務(wù)器數(shù)量少于某個(gè)數(shù)值)梢睛;
· 檢測(cè)命令丟失(根據(jù)復(fù)制偏移量的對(duì)比)肥印;

【參考】
[1] 《Redis設(shè)計(jì)與實(shí)現(xiàn)》

歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處wenmingxing Redis之主從服務(wù)器的復(fù)制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绝葡,一起剝皮案震驚了整個(gè)濱河市深碱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藏畅,老刑警劉巖敷硅,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件功咒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绞蹦,警方通過(guò)查閱死者的電腦和手機(jī)力奋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)幽七,“玉大人景殷,你說(shuō)我怎么就攤上這事≡杪牛” “怎么了猿挚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)驶鹉。 經(jīng)常有香客問(wèn)我绩蜻,道長(zhǎng),這世上最難降的妖魔是什么室埋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任办绝,我火速辦了婚禮,結(jié)果婚禮上词顾,老公的妹妹穿的比我還像新娘八秃。我一直安慰自己碱妆,他們只是感情好肉盹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著疹尾,像睡著了一般上忍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纳本,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天窍蓝,我揣著相機(jī)與錄音,去河邊找鬼繁成。 笑死吓笙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的巾腕。 我是一名探鬼主播面睛,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼尊搬!你這毒婦竟也來(lái)了叁鉴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤佛寿,失蹤者是張志新(化名)和其女友劉穎幌墓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡常侣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年蜡饵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胳施。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡验残,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巾乳,到底是詐尸還是另有隱情您没,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布胆绊,位于F島的核電站氨鹏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏压状。R本人自食惡果不足惜仆抵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望种冬。 院中可真熱鬧镣丑,春花似錦、人聲如沸娱两。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)十兢。三九已至趣竣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間旱物,已是汗流浹背遥缕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宵呛,地道東北人单匣。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宝穗,于是被迫代替她去往敵國(guó)和親户秤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354