Redis如何高效可靠地实现主从复制?终于有人讲明白了
導(dǎo)讀:本文內(nèi)容主要包括Redis主從復(fù)制功能的概述、作用和方案實(shí)施。
?
作者:李樂 來源:大數(shù)據(jù)DT(ID:bigdatadt)?
?
Redis支持主從復(fù)制功能,用戶可以通過執(zhí)行slaveof命令或者在配置文件中設(shè)置slaveof選項(xiàng)來開啟復(fù)制功能。例如,現(xiàn)在有兩臺(tái)服務(wù)器—127.0.0.1:6379和127.0.0.1:7000,向服務(wù)器127.0.0.1:6379發(fā)送下面命令:
?
127.0.0.1:6379>slaveof?127.0.0.1?7000 OK?
此時(shí)服務(wù)器127.0.0.1:6379會(huì)成為服務(wù)器127.0.0.1:7000的從服務(wù)器(slaver),服務(wù)器127.0.0.1:7000會(huì)成為服務(wù)器127.0.0.1:6379的主服務(wù)器(master);通過復(fù)制功能,從服務(wù)器127.0.0.1:6379的數(shù)據(jù)可以和主服務(wù)器127.0.0.1:7000的數(shù)據(jù)保持同步。
?
為什么需要主從復(fù)制功能呢?
?
簡單來說,主從復(fù)制功能主要有以下兩點(diǎn)作用。
?
讀寫分離,單臺(tái)服務(wù)器能支撐的QPS是有上限的,我們可以部署一臺(tái)主服務(wù)器、多臺(tái)從服務(wù)器,主服務(wù)器只處理寫請(qǐng)求,從服務(wù)器通過復(fù)制功能同步主服務(wù)器數(shù)據(jù),只處理讀請(qǐng)求,以此提升Redis服務(wù)能力;另外我們還可以通過復(fù)制功能來讓主服務(wù)器免于執(zhí)行持久化操作:只要關(guān)閉主服務(wù)器的持久化功能,然后由從服務(wù)器去執(zhí)行持久化操作即可。
數(shù)據(jù)容災(zāi),任何服務(wù)器都有宕機(jī)的可能,我們同樣可以通過主從復(fù)制功能提升Redis服務(wù)的可靠性;由于從服務(wù)器與主服務(wù)器數(shù)據(jù)保持同步,一旦主服務(wù)器宕機(jī),可以立即將請(qǐng)求切換到從服務(wù)器,從而避免Redis服務(wù)中斷。
?
對(duì)于本例來說slaveof命令的主要流程如下。
?
從服務(wù)器127.0.0.1:6379向主服務(wù)器127.0.0.1:7000發(fā)送sync命令,請(qǐng)求同步數(shù)據(jù)。
主服務(wù)器127.0.0.1:7000接收到sync命令請(qǐng)求,開始執(zhí)行bgsave命令持久化數(shù)據(jù)到RDB文件,并且在持久化數(shù)據(jù)期間會(huì)將所有新執(zhí)行的寫入命令都保存到一個(gè)緩沖區(qū)。
當(dāng)持久化數(shù)據(jù)執(zhí)行完畢后,主服務(wù)器127.0.0.1:7000將該RDB文件發(fā)送給從服務(wù)器127.0.0.1:6379,從服務(wù)器接收該RDB文件,并將文件中的數(shù)據(jù)加載到內(nèi)存。
主服務(wù)器127.0.0.1:7000將緩沖區(qū)中的命令請(qǐng)求發(fā)送給從服務(wù)器127.0.0.1:6379。
每當(dāng)主服務(wù)器127.0.0.1:7000接收到寫命令請(qǐng)求時(shí),都會(huì)將該命令請(qǐng)求按照Redis協(xié)議格式發(fā)送給從服務(wù)器127.0.0.1:6379,從服務(wù)器接收并處理主服務(wù)器發(fā)送過來的命令請(qǐng)求。
?
上述流程已經(jīng)可以完成主從復(fù)制基本功能了,Redis 2.8以前就是這樣實(shí)現(xiàn)的,但是注意到步驟2中存在持久化操作(bgsave),而這是一個(gè)非常耗費(fèi)資源的操作。
?
?
舉一個(gè)簡單的例子。
?
主服務(wù)器和從服務(wù)器之間是通過TCP長連接交互數(shù)據(jù)的,假設(shè)某個(gè)時(shí)刻主從服務(wù)器之間的網(wǎng)絡(luò)連接發(fā)生故障且時(shí)間比較短,在此期間主服務(wù)器只執(zhí)行了很少的寫命令請(qǐng)求。
?
待主從服務(wù)器之間的網(wǎng)絡(luò)連接恢復(fù)后,從服務(wù)器會(huì)重新連接到主服務(wù)器,并發(fā)送sync命令請(qǐng)求同步數(shù)據(jù)。這時(shí)候主服務(wù)器還需要執(zhí)行持久化操作嗎?顯然是可以避免的,只要主服務(wù)器能夠緩存連接故障期間執(zhí)行的寫命令即可。
?
Redis 2.8提出了新的主從復(fù)制解決方案。從服務(wù)器會(huì)記錄已經(jīng)從主服務(wù)器接收到的數(shù)據(jù)量(復(fù)制偏移量);而主服務(wù)器會(huì)維護(hù)一個(gè)復(fù)制緩沖區(qū),記錄自己已執(zhí)行且待發(fā)送給從服務(wù)器的命令請(qǐng)求,同時(shí)還需要記錄復(fù)制緩沖區(qū)第一個(gè)字節(jié)的復(fù)制偏移量。從服務(wù)器請(qǐng)求同步主服務(wù)器的命令也改為了psync。
?
當(dāng)從服務(wù)器連接到主服務(wù)器時(shí),會(huì)向主服務(wù)器發(fā)送psync命令請(qǐng)求同步數(shù)據(jù),同時(shí)告訴主服務(wù)器自己已經(jīng)接收到的復(fù)制偏移量,主服務(wù)器判斷該復(fù)制偏移量是否還包含在復(fù)制緩沖區(qū);如果包含,則不需要執(zhí)行持久化操作,直接向從服務(wù)器發(fā)送復(fù)制緩沖區(qū)中命令請(qǐng)求即可,這稱為部分重同步;如果不包含,則需要執(zhí)行持久化操作,同時(shí)將所有新執(zhí)行的寫命令緩存在復(fù)制緩沖區(qū)中,并重置復(fù)制緩沖區(qū)第一個(gè)字節(jié)的復(fù)制偏移量,這稱為完整重同步。
?
詳情可參照Redis源碼,方法masterTryPartialResynchronization用于判斷是否可以執(zhí)行部分重同步;方法replicationFeedSlaves用于向所有從服務(wù)器廣播命令。
?
void?replicationFeedSlaves(list?*slaves,?int?dictid,?robj?**argv,?int?argc){if?(server.repl_backlog)?{//將當(dāng)前命令請(qǐng)求添加到復(fù)制緩沖區(qū)}while((ln?=?listNext(&li)))?{//向所有從服務(wù)器同步命令請(qǐng)求} }?
另外,從服務(wù)器也會(huì)通過命令“REPLCONF ACK < reploff >”定時(shí)向主服務(wù)器匯報(bào)自己的復(fù)制偏移量;據(jù)此,主服務(wù)器一來可以檢測(cè)從服務(wù)器是否有效,二來可以重新廣播丟失的命令請(qǐng)求。
?
另外需要注意的是,每臺(tái)Redis服務(wù)器都有一個(gè)運(yùn)行ID,從服務(wù)器每次發(fā)送psync請(qǐng)求同步數(shù)據(jù)時(shí),會(huì)攜帶自己需要同步主服務(wù)器的運(yùn)行ID。
?
主服務(wù)器接收到psync命令時(shí),需要判斷命令參數(shù)運(yùn)行ID與自己的運(yùn)行ID是否相等,只有相等才有可能執(zhí)行部分重同步。而當(dāng)從服務(wù)器首次請(qǐng)求主服務(wù)器同步數(shù)據(jù)時(shí),從服務(wù)器顯然是不知道主服務(wù)器的運(yùn)行ID,此時(shí)運(yùn)行ID以“?”填充,同時(shí)復(fù)制偏移量初始化為-1。
?
從上面的分析我們可以得到psync命令格式為“psync <MASTER_RUN_ID> <OFFSET>”,主從復(fù)制初始化流程如圖1所示。
?
從圖1可以看到,當(dāng)主服務(wù)器判斷可以執(zhí)行部分重同步時(shí)向從服務(wù)器返回“+CON-TINUE”;需要執(zhí)行完整重同步時(shí)向從服務(wù)器返回“+FULLRESYNC RUN_ID OFFSET”,其中RUN_ID為主服務(wù)器自己的運(yùn)行ID,OFFSET為復(fù)制偏移量。
▲圖1 主從復(fù)制初始化流程圖
?
可以看到執(zhí)行部分重同步的要求還是比較嚴(yán)格的:
?
RUN_ID必須相等;
復(fù)制偏移量必須包含在復(fù)制緩沖區(qū)中。
?
然而在生產(chǎn)環(huán)境中,經(jīng)常會(huì)出現(xiàn)以下兩種情況:
?
-
從服務(wù)器重啟(復(fù)制信息丟失);
-
主服務(wù)器故障導(dǎo)致主從切換(從多個(gè)從服務(wù)器重新選舉出一臺(tái)機(jī)器作為主服務(wù)器,主服務(wù)器運(yùn)行ID發(fā)生改變)。
?
這時(shí)顯然是無法執(zhí)行部分重同步的,而這兩種情況又很常見,因此Redis 4.0針對(duì)主從復(fù)制又提出了兩點(diǎn)優(yōu)化,提出了psync2協(xié)議。
?
-
方案1:持久化主從復(fù)制信息
?
Redis服務(wù)器關(guān)閉時(shí),將主從復(fù)制信息(復(fù)制的主服務(wù)器RUN_ID與復(fù)制偏移量)作為輔助字段存儲(chǔ)在RDB文件中;Redis服務(wù)器啟動(dòng)加載RDB文件時(shí),恢復(fù)主從復(fù)制信息,重新同步主服務(wù)器時(shí)攜帶。持久化主從復(fù)制信息代碼如下:
?
if?(rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid)==?-1)?return?-1;if?(rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset)==?-1)?return?-1;?
-
方案2:存儲(chǔ)上一個(gè)主服務(wù)器復(fù)制信息
?
當(dāng)主服務(wù)器發(fā)生故障,自己成為新的主服務(wù)器時(shí),使用變量server.replid2和server.second_replid_offset存儲(chǔ)之前主服務(wù)器的運(yùn)行ID與復(fù)制偏移量:
?
void?shiftReplicationId(void)?{memcpy(server.replid2,server.replid,sizeof(server.replid));server.second_replid_offset?=?server.master_repl_offset+1;changeReplicationId(); }?
另外判斷是否能執(zhí)行部分重同步的條件也改變?yōu)?#xff1a;
?
if?(strcasecmp(master_replid,?server.replid)?&&(strcasecmp(master_replid,?server.replid2)?||psync_offset?>?server.second_replid_offset)) {goto?need_full_resync; }?
假設(shè)m為主服務(wù)器(運(yùn)行ID為M_ID),A、B和C為三個(gè)從服務(wù)器;某一時(shí)刻主服務(wù)器m發(fā)生故障,從服務(wù)器A升級(jí)為主服務(wù)器(同時(shí)會(huì)記錄replid2=M_ID),從服務(wù)器B和C重新向主服務(wù)器A發(fā)送“psync M_ID psync_offset”請(qǐng)求;顯然根據(jù)上面條件,只要psync_offset滿足條件,就可以執(zhí)行部分重同步。
?
關(guān)于作者:李樂,好未來PHP工程師,西安電子科技大學(xué)碩士,樂于鉆研技術(shù)與源碼研究,對(duì)Redis和Nginx有較深理解。合著書籍《Redis 5設(shè)計(jì)與源碼分析》。?
本文摘編自《Redis 5設(shè)計(jì)與源碼分析》,經(jīng)出版方授權(quán)發(fā)布。?
延伸閱讀《Redis 5設(shè)計(jì)與源碼分析》 點(diǎn)擊上圖了解及購買 轉(zhuǎn)載請(qǐng)聯(lián)系微信:DoctorData?
推薦語:好未來、滴滴、百度等公司專家聯(lián)合撰寫,掌握Redis 5設(shè)計(jì)與命令實(shí)現(xiàn),透徹掌握分布式緩存。深入理解Redis 5設(shè)計(jì)精髓。本書系統(tǒng)講解Redis 5設(shè)計(jì)、數(shù)據(jù)結(jié)構(gòu)、底層命令實(shí)現(xiàn),以及持久化、主從復(fù)制、集群的實(shí)現(xiàn)。?
?
有話要說??
Q:?關(guān)于Redis主從復(fù)制,你還有哪些疑問? 歡迎留言與大家分享?
猜你想看??
-
?
-
?
-
?
-
?
?
更多精彩??
在公眾號(hào)對(duì)話框輸入以下關(guān)鍵詞 查看更多優(yōu)質(zhì)內(nèi)容!?
PPT?|?報(bào)告?|?讀書?|?書單?|?干貨? 大數(shù)據(jù)?|?揭秘?|?Python?|?可視化 AI?|?人工智能?|?5G?|?中臺(tái) 機(jī)器學(xué)習(xí)?|?深度學(xué)習(xí)?|?神經(jīng)網(wǎng)絡(luò) 合伙人?|?1024?|?段子?|?數(shù)學(xué)?
據(jù)統(tǒng)計(jì),99%的大咖都完成了這個(gè)神操作 ??
?
覺得不錯(cuò),請(qǐng)把這篇文章分享給你的朋友 轉(zhuǎn)載 / 投稿請(qǐng)聯(lián)系:baiyu@hzbook.com 更多精彩,請(qǐng)?jiān)诤笈_(tái)點(diǎn)擊“歷史文章”查看點(diǎn)擊閱讀原文,了解更多
總結(jié)
以上是生活随笔為你收集整理的Redis如何高效可靠地实现主从复制?终于有人讲明白了的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是YARN?跟HBase和Spark
- 下一篇: 2.14情人节,程序员该如何绝地反击?