Redis4.0,增加了新特性PSync 2(部分同步版本2);主要解決Redis運(yùn)維管理中從實(shí)例重啟、主實(shí)例故障轉(zhuǎn)移等場景導(dǎo)致的全重同步問題。
1什么是Redis部分再同步-psync
Redis部分重新同步:當(dāng)復(fù)制由于某種原因中斷后,從庫中重新同步redis時(shí),只同步主實(shí)例的差異數(shù)據(jù)(寫指令),復(fù)制整個(gè)RDB文件而不進(jìn)行bgsave。
本文中的名詞約定:
部分再同步:以下簡稱psync
完全再同步:以下稱為完全同步
Redis2.8版本1部分再同步:以下簡稱psync1
Redis4.0版本2部分再同步:以下簡稱psync2。
在解釋psync2的功能之前,先簡單解釋一下redis2.8版本發(fā)布的psync1。
Redis2.8 psync1解決了什么問題
在psync1函數(shù)出現(xiàn)之前,第二級的redis復(fù)制中斷從實(shí)例觸發(fā)了fullsync。
每次fullsync,集群的性能和資源使用都可能帶來抖動;如果redis處于不穩(wěn)定的網(wǎng)絡(luò)環(huán)境中,fullsync的傳出頻率可能會很高。為了解決這個(gè)問題,redis2.8引入了psync1,有效地解決了這種拷貝閃存的影響。Redis的fullsync對業(yè)務(wù)的影響相對“沉重”;性能和可用性存在一定的風(fēng)險(xiǎn)。
以下是fullsync的一些常見效果:
Master需要運(yùn)行bgsave,出現(xiàn)fork(),可能導(dǎo)致master在毫秒或秒內(nèi)堵塞(latest_fork_usec狀態(tài)監(jiān)控);
redis進(jìn)程分叉導(dǎo)致寫時(shí)復(fù)制內(nèi)存使用(以下簡稱COW)的消耗,最多可以導(dǎo)致主進(jìn)程內(nèi)存使用的消耗。(在eg日志中輸出RDB:寫入時(shí)復(fù)制使用5213兆內(nèi)存)
redis從負(fù)載RDB進(jìn)程會導(dǎo)致復(fù)制線程的客戶端輸出緩沖區(qū)大幅增長;增加主進(jìn)程的內(nèi)存消耗;
Redis節(jié)省了RDB(不考慮丟失復(fù)制),導(dǎo)致服務(wù)器磁盤IO和CPU(壓縮)資源的消耗
發(fā)送幾千兆字節(jié)的RDB文件將導(dǎo)致服務(wù)器網(wǎng)絡(luò)出口的爆炸。如果使用千兆網(wǎng)卡服務(wù)器,會影響正常的服務(wù)請求響應(yīng)時(shí)間(以及其他連鎖效應(yīng))
psync1的基本實(shí)現(xiàn)因?yàn)閜sync2是基于psync1的增強(qiáng)實(shí)現(xiàn),所以在介紹psync2之前,先簡單分析一下psync1的實(shí)現(xiàn)。
為了支持psync1,redis2.8引入了復(fù)制積壓緩沖區(qū)(以下簡稱復(fù)制積壓緩沖區(qū));復(fù)制積壓緩沖區(qū)是redis維護(hù)的固定長度緩沖區(qū)隊(duì)列(由參數(shù)repl-backlog-size設(shè)置,默認(rèn)值為1MB),主機(jī)的寫命令與從機(jī)同步,緩沖區(qū)中寫入一個(gè)副本(主機(jī)只有一個(gè)積壓緩沖區(qū),由所有從機(jī)共享)。
redis復(fù)制中斷時(shí),從機(jī)會嘗試使用psync來報(bào)告當(dāng)前同步的主機(jī)的原始主機(jī)runid+offset(復(fù)制offset,類似于mysql binlog文件和位置);
如果runid與master的一致,且拷貝偏移量仍在master的拷貝積壓緩沖區(qū)(即偏移量>:= min(積壓值),則master認(rèn)為部分再同步成功,不會進(jìn)行完全同步。
部分重新同步成功。主日志顯示如下:30422: m04aug 14: 33: 48.505 *從xxxxx: 10005請求同步30422: m04aug 14: 33: 48.506 *接受來自xxx: 10005的部分再同步請求。從偏移量6448313開始發(fā)送0字節(jié)的積壓。
redis2.8的部分同步機(jī)制有效解決了網(wǎng)絡(luò)環(huán)境不穩(wěn)定和redis執(zhí)行高時(shí)間復(fù)雜度命令導(dǎo)致的復(fù)制中斷,導(dǎo)致完全同步。然而,在處理從機(jī)重啟和主機(jī)故障轉(zhuǎn)移的情況時(shí),psync1仍然需要完全同步。
psync1的缺點(diǎn)
從上面可以看出,psync1需要同時(shí)滿足兩個(gè)條件才能成功:主運(yùn)行id不變,復(fù)制偏移量在主復(fù)制產(chǎn)品緩沖區(qū)中。
然后,當(dāng)Redissslave重新啟動時(shí),主runid和副本偏移量將丟失,因此需要完全重新同步;redis主機(jī)故障轉(zhuǎn)移,因?yàn)橹鳈C(jī)runid已更改;故障切換后,新的從機(jī)需要完全重新同步。而從維護(hù)重啟和主故障轉(zhuǎn)移是redis運(yùn)維的常見場景,針對redis的psync1無法解決這兩種場景的部分再同步成功問題。
所以redis4.0 -psync2的增強(qiáng)型部分再同步功能主要解決這兩種場景的部分再同步。
2 psync2實(shí)現(xiàn)簡介
在redis集群的實(shí)際生產(chǎn)操作中,實(shí)例的維護(hù)重啟和主實(shí)例的故障轉(zhuǎn)移(如集群故障轉(zhuǎn)移)是常見的操作(如實(shí)例升級、重命名命令和釋放實(shí)例內(nèi)存碎片等)。).在redis 4.0版本之前,這種維護(hù)處理,Redis會有完全的重新同步,導(dǎo)致對性能敏感服務(wù)的少量破壞。
如前所述,psync2主要使redis能夠在從屬實(shí)例重啟和主實(shí)例故障轉(zhuǎn)移的情況下使用部分重新同步。在本節(jié)中,將簡要描述psync2在這兩種情況下的邏輯實(shí)現(xiàn)。
名詞解釋:
Master_replid:重復(fù)ID1(以下簡稱replid1),長度為41字節(jié)(40個(gè)隨機(jī)字符串+'0 ')的字符串。還有所有redis實(shí)例,都和runid沒有直接關(guān)系,但是都是getRandomHexChars函數(shù)生成的,和runid生成規(guī)則一樣。當(dāng)一個(gè)實(shí)例成為從實(shí)例時(shí),它自己的replid1將被主實(shí)例的replid1覆蓋。
Master_replID2:復(fù)制ID2(以下稱為replID2),默認(rèn)初始化為全零,用于存儲最后一個(gè)主實(shí)例的replid1
可以通過信息復(fù)制查看實(shí)例的replid信息;例子如下:
127.0.0.1:6385 >信息復(fù)制# Replicationrole:slave master _ host:xxxx//IP模糊處理master _ port:6382 master _ link _ status:up slave _ repl _ offset:119750 master _ repl id:Fe 093 add 4 ab 71544 ce 6508 d2e 0 B1 FD 0 b 7d 7 D1 C5 b 2//這是主實(shí)例的相同master _ repl id 1:00000000000000000000000000000000000000000000000000000000000000000
在以前的版本中,redis重啟后,復(fù)制信息完全丟失;因此,從實(shí)例重新啟動后,只能執(zhí)行完全重新同步。
Redis4.0重啟后仍然可以進(jìn)行部分再同步,主要做以下三點(diǎn):
當(dāng)redis關(guān)閉時(shí),復(fù)制信息作為輔助字段存儲在RDB文件中。從而實(shí)現(xiàn)同步信息持久性;
當(dāng)redis開始加載RDB文件時(shí),它會將復(fù)制信息分配給相關(guān)字段;
redis再同步時(shí),會上報(bào)repl-id和repl-offset的同步信息。如果它與主實(shí)例匹配,并且偏移量仍在主實(shí)例的復(fù)制積壓緩沖區(qū)中,則只會執(zhí)行部分重新同步。
接下來,我們詳細(xì)分析每個(gè)步驟的簡單實(shí)現(xiàn)
當(dāng)redis關(guān)閉時(shí),持續(xù)將信息復(fù)制到RDB
redis關(guān)閉時(shí),關(guān)閉保存將調(diào)用rdbSaveInfoAuxFields函數(shù)。
將當(dāng)前實(shí)例的復(fù)制標(biāo)識和復(fù)制偏移量保存到RDB文件中。
注意:當(dāng)前RDB存儲的數(shù)據(jù)內(nèi)容和復(fù)制信息是一致的。熟悉MySQL的同學(xué)可以認(rèn)為MySQL中的備份總數(shù)與binlog信息一致。
rdbSaveInfoAuxFields函數(shù)在rdb.c源文件中實(shí)現(xiàn),省略的代碼如下:
/*保存幾個(gè)默認(rèn)的輔助字段,其中包含RDB生成的信息。*/int rdbSaveInfoAuxFields(Rio * rdb,int flags,rdbSaveInfo *rsi) { /*添加幾個(gè)關(guān)于RDB創(chuàng)建時(shí)的狀態(tài)的字段。*/if(rdbsaveauxfieldstr(RDB," redis-ver ",REDIS _ VERSION)= =-1)return-1;//將實(shí)例的repl-id和repl-offset作為輔助字段存儲在RDB if (rdbsaveauxfieldstr (RDB,“repl-id”,服務(wù)器。replid = =-1)return-1;if (rdbSaveAuxFieldStrInt(rdb," repl-offset ",server . master _ repl _ offset)= =-1)return-1;返回1;}
對于生成的RDB文件,可以通過redis-check-RDB工具查看輔助字段信息。
復(fù)制器信息的兩個(gè)字段與信息中的字段相同;
$shell>。/src/redis-check-RDB dump . RDB[offset 0]Checking RDB文件dump . RDB[offset 26]AUX FIELD redis-ver = ' 4 . 0 . 1 '[offset 133]AUX FIELD repl-id = ' 44873 f 839 AE 3a 57572920 cdaf 70399672 b 842691 '[offset 148]AUX FIELD repl-offset = ' 0 '[offset 167]o/RDB看起來還可以!0/[信息] 1密鑰讀取[信息] 0過期[信息] 0已經(jīng)過期redis開始讀取RDB的復(fù)制信息
redis實(shí)例開始讀取RDB文件,這是通過rdb.c文件中的rdbLoadRio()函數(shù)實(shí)現(xiàn)的。
redis加載RDB文件時(shí),會處理文件中輔助字段的信息,并將repl_id和repl_offset加載到實(shí)例中,并分別賦予master_replid和master_repl_offset兩個(gè)變量值。
下面的代碼從RDB文件中讀取兩個(gè)輔助字段值。
Int rdbloadrio (Rio * RDB,rdbsaveinfo * RSI){-省略-else if(!strcasecmp(auxkey->;Ptr," repl-id")) {//讀取的aux字段為repl-id if(RSI & amp:& amp;sdslen(auxval->;ptr = = CONFIG _ RUN _ ID _ SIZE){ memcpy(RSI-& gt;repl_id,auxval->ptr,CONFIG _ RUN _ ID _ SIZE+1);rsi->。repl _ id _ is _ set = 1;} } else if(!strcasecmp(auxkey->;ptr," repl-offset")) { if (rsi) rsi->。repl_offset = strtoll(auxval->;ptr,NULL,10);} else { /*我們忽略我們不理解的字段,如AUX字段*合同。*/服務(wù)器日志(LL_DEBUG),"無法識別的RDB輔助字段:“%s”,(char *)AUX key->;ptr);}} redis試圖從實(shí)例進(jìn)行部分重新同步
重新啟動redis實(shí)例后,從RDB文件加載Master_replid和master _ repl _ offset(注意:這里不討論AOF和RDB加載優(yōu)先級);等效于實(shí)例的server.cached_master。當(dāng)我們將其視為一個(gè)實(shí)例的從庫(包括被動集群從或主動執(zhí)行slaveof指令)時(shí),該實(shí)例向主實(shí)例報(bào)告master_replid和master _ repl _ offset+1;當(dāng)實(shí)例同時(shí)滿足以下兩個(gè)條件時(shí),部分重新同步是可能的:
1.來自實(shí)例的report master_replid字符串,它等于主實(shí)例的master_replid1或replid2
2.從實(shí)例報(bào)告的master_repl_offset+1字節(jié)仍然存在于主實(shí)例的復(fù)制積壓緩沖區(qū)中
請嘗試從實(shí)例(在replication.c文件中)部分重新同步slavertrypartialresynchronization函數(shù);
主實(shí)例判斷是否可以執(zhí)行部分再同步功能MasterTrypArtialSynchronization(在replication.c文件中)。
redis重新啟動時(shí),臨時(shí)調(diào)整主實(shí)例的復(fù)制積壓緩沖區(qū)大小
redis的復(fù)制積壓緩沖區(qū)由參數(shù)repl-backlog-size設(shè)置,為1MB默認(rèn)情況下。為了確保從實(shí)例重新啟動后的部分重新同步,應(yīng)該設(shè)置一個(gè)合理的repl-backlog-size值。
1計(jì)算合理的復(fù)件積壓量值
根據(jù)以主庫每秒為增量的主復(fù)制偏移量master _ repl _ offset(由信息復(fù)制指令獲得),
如果偏移量每秒增加5MB,主實(shí)例副本的backlog緩沖區(qū)應(yīng)該保留最后60秒寫入的內(nèi)容,backlog_size設(shè)置應(yīng)該大于300MB(60*5)。但是,從實(shí)例中重新啟動和加載RDB文件是一個(gè)耗時(shí)的過程。如果重啟某個(gè)實(shí)例需要120秒(RDB大小與CPU配置有關(guān)),那么主實(shí)例的backlog_size必須設(shè)置為至少600MB。
計(jì)算公式:backlog_size =重啟從實(shí)例時(shí)間*主實(shí)例偏移量每秒寫入量
2在重新啟動從屬實(shí)例之前,調(diào)整主實(shí)例的動態(tài)調(diào)整repl-backlog-size值。
因?yàn)楫?dāng)redis的repl-backlog-size通過配置集動態(tài)調(diào)整時(shí),redis會釋放當(dāng)前的backlog緩沖區(qū),并重新分配一個(gè)指定大小的緩沖區(qū)。因此,在重新啟動從屬實(shí)例之前,我們必須調(diào)整主實(shí)例的repl-backlog-size。
調(diào)整backlog_size處理函數(shù)resizeReplicationBacklog,代碼邏輯如下:
void ResizeReplicationbacklog(long long newsize){ if(newsize & lt;config _ repl _ backlog _ min _ size)//如果設(shè)置的新值小于16 KB,則修改為16KB新大小= config _ repl _ backlog _ min _ sizeif(server . repl _ backlog _ size = = newsize)return;//如果新值與原始值相同,則不做任何處理返回。server . repl _ backlog _ size = newsize;//如果(server.repl_backlog!= NULL) {//當(dāng)backlog的內(nèi)容不是空時(shí),釋放當(dāng)前backlog。并根據(jù)新的值/*分配一個(gè)新的積壓,我們實(shí)際上要做的是刷新舊的緩沖區(qū),并重新鎖定一個(gè)新的*空緩沖區(qū)。它將隨著新數(shù)據(jù)的增加而增加。*原因是復(fù)制幾千兆字節(jié)會增加延遲,更糟糕的是,我們經(jīng)常需要在釋放舊緩沖區(qū)之前分配額外的空間。*/zfree(server . repl _ backlog);server . repl _ backlog = zmalloc(server . repl _ backlog _ size);server . repl _ backlog _ hist len = 0;//將backlog內(nèi)容的長度和第一個(gè)字節(jié)的偏移量修改為0 server.repl _ backlog _ idx = 0/*我們的下一個(gè)字節(jié)是...因?yàn)榫彌_區(qū)是空的。*/server . repl _ backlog _ off = server . master _ repl _ offset+1;}} 3 psync2實(shí)現(xiàn)Redis集群故障轉(zhuǎn)移的部分新同步
為了解決主實(shí)例故障轉(zhuǎn)移后重新同步新主實(shí)例數(shù)據(jù)時(shí)使用psync的問題,fullsync改為使用。
1 redis4.0使用兩組replid和offset來替換原來的master runid和offset。
2 Redissslave默認(rèn)開啟復(fù)印積壓緩沖功能;以便其他后向從設(shè)備在從設(shè)備故障轉(zhuǎn)移后更改主設(shè)備時(shí),可以從緩沖區(qū)獲取寫指令。
第一組:master_replid和master_repl_offset
如果redis是主實(shí)例,則表示為自己的replid和復(fù)制偏移量;如果redis是一個(gè)從實(shí)例,它被表示為自己的主實(shí)例的replid1和同步主實(shí)例的復(fù)制偏移量。
第二組:master_replid2和second_repl_offset
無論主實(shí)例和從實(shí)例是什么,它們都代表它們的最后一個(gè)主實(shí)例repid1和復(fù)制偏移量;用于同級實(shí)例或級聯(lián)復(fù)制、主庫故障轉(zhuǎn)移psync。
初始化時(shí),前者為40個(gè)字符,長度為0,后者為-1;只有當(dāng)主實(shí)例進(jìn)行故障轉(zhuǎn)移時(shí),redis才會將其replid1和master_repl_offset+1分別分配給master_replid2和second_repl_offset。
這個(gè)切換邏輯在函數(shù)shiftReplicationId中實(shí)現(xiàn)。
void shift ReplicationId(void){ memcpy(server.replid 2,server . replid,sizeof(server . replid));//replid分配給replid2/*我們將第二個(gè)replid偏移量設(shè)置為主偏移量+1。由于*從機(jī)將請求尚未收到的第一個(gè)字節(jié),因此*我們需要向偏移量添加一個(gè)字節(jié):例如,如果作為從機(jī),我們*確定我們與主機(jī)有相同的50個(gè)字節(jié)的歷史,在我們*變成主機(jī)后,我們可以接受偏移量為* 51的PSYNC請求,因?yàn)閺臋C(jī)請求直到第50個(gè)*字節(jié)都有相同的歷史,并且正在請求從偏移量51開始的新字節(jié)。*/server . second _ replid _ offset = server . master _ repl _ offset+1;changeReplicationId();服務(wù)器日志(LL_WARNING),"將輔助復(fù)制標(biāo)識設(shè)置為%s,在偏移量%lld之前有效。新復(fù)制ID為%s ",server.replid2,server.second_replid_offset,server . replid);}
通過這種方式,主庫可以進(jìn)行故障轉(zhuǎn)移,以下三種常見結(jié)構(gòu)可以執(zhí)行psync:
1一主一從開關(guān),a->: B開關(guān)變成B->:A;
2一主多從切換時(shí),兄弟節(jié)點(diǎn)變成父子節(jié)點(diǎn);
3級復(fù)制被切換,a->:B->;C開關(guān)變成b->:C->;A
主實(shí)例判斷psync的邏輯功能是否可以在mastertrypartiresynchronization()中執(zhí)行
int master try manual synchronization(client * c){//如果從機(jī)提供的master_replid與主機(jī)的replid不同,與主機(jī)的replid2不同,或者同步速度比主機(jī)快;full sync . if(str escmp(master _ replid,server . replid)&:& amp;(str secmp(master _ replid,server . replid 2)| | psync _ offset & gt;server . second _ replid _ offset)){/* Run id "?"由想要強(qiáng)制完全重新同步的從機(jī)使用。*/ if (master_replid[0]!= '?'){ if(str cascmp(master _ replid,server . replid)& amp;& ampstr secmp(master_replid,server.replid 2)){ Serverlog(LL _ NOTICE,"不接受部分重新同步:" "復(fù)制id不匹配(從機(jī)要求' %s ',我的" "復(fù)制id為' %s '和' %s ')",master _ replid,server . replid,server . replid 2);} else { serverLog(LL_NOTICE,“不接受部分重新同步:“”第二個(gè)ID的請求偏移量為%lld,但我可以回復(fù)“”,最多%lld”,psync_offset,server . second _ replid _ offset);} } else { serverLog(LL_NOTICE,“從%s請求完全重新同步”,replicationGetslavename(c));} goto need _ full _ resync} /*我們還有奴隸要的數(shù)據(jù)?*/ if(!server . repl _ backlog | | psync _ offset & lt;server . repl _ backlog _ off | | psync _ offset & gt;(server . repl _ backlog _ off+server . repl _ backlog _ histlen)){ Serverlog(LL _ NOTICE,“由于缺少backlog,無法與從屬%s部分重新同步(從屬請求為:%lld)。”,replicationGetSlaveName(c),psync _ offset);if (psync_offset >;server . master _ repl _ offset){ ServerLog(LL _ WARNING),"警告:從%s試圖使用大于主復(fù)制偏移量的偏移量進(jìn)行PSYNC。",replicationGetSlaveName(c));} goto need _ full _ resync}
-結(jié)束-
推薦訂閱原作者微信官方賬號DBACoder
▼
1.《repl Redis4.0新特性-PSYNC2》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識,僅代表作者本人觀點(diǎn),與本網(wǎng)站無關(guān),侵刪請聯(lián)系頁腳下方聯(lián)系方式。
2.《repl Redis4.0新特性-PSYNC2》僅供讀者參考,本網(wǎng)站未對該內(nèi)容進(jìn)行證實(shí),對其原創(chuàng)性、真實(shí)性、完整性、及時(shí)性不作任何保證。
3.文章轉(zhuǎn)載時(shí)請保留本站內(nèi)容來源地址,http://f99ss.com/yule/1444782.html