淺談Redis主從復(fù)制[轉(zhuǎn)載]

淺談Redis主從復(fù)制

2013.09.27 11:27:00 來(lái)源: 京東 作者:張成遠(yuǎn) ( 0 條評(píng)論 )

Redis是一個(gè)開(kāi)源的嫉入,遵守BSD許可協(xié)議的key/value緩存系統(tǒng),并由其高效的響應(yīng)速度以及豐富的數(shù)據(jù)結(jié)構(gòu)而聞名觉至。Redis在京東的使用也是非常普遍的剔应,包括很多關(guān)鍵業(yè)務(wù)上的使用,由于Redis官方集群還未發(fā)布康谆,在使用Redis的過(guò)程中需要面對(duì)Redis的單點(diǎn)問(wèn)題领斥,京東采用的是一種比較通用的解決方案即由主從備份再加相應(yīng)的主從切換(在一些場(chǎng)景下可能進(jìn)行讀寫(xiě)分離),使主Redis出現(xiàn)失效的時(shí)候可以快速的切換到從Redis上沃暗。但Redis目前存在的一個(gè)問(wèn)題是主從復(fù)制在遇到網(wǎng)絡(luò)不穩(wěn)定的情況下,Slave和Master斷開(kāi)(包括閃斷)會(huì)導(dǎo)致Master需要將內(nèi)存中的數(shù)據(jù)全部重新生成rdb文件(快照文件)何恶,然后傳輸給Slave孽锥。Slave接收完Master傳遞過(guò)來(lái)的rdb文件以后會(huì)將自身的內(nèi)存清空,把rdb文件重新加載到內(nèi)存中细层。這種方式效率比較低下惜辑,尤其是在數(shù)據(jù)量大的情況下,畢竟網(wǎng)絡(luò)閃斷未必丟數(shù)據(jù)或者說(shuō)丟的數(shù)據(jù)只是少部分疫赎,但卻要為此付出將整個(gè)內(nèi)存數(shù)據(jù)都重新傳輸一次的代價(jià)盛撑。如果能夠?qū)㈤W斷過(guò)程的更新數(shù)據(jù)傳遞給Slave,那么就不需要將Master內(nèi)存中的所有數(shù)據(jù)都傳遞給Slave了捧搞。Redis作者在2.8的候選版(以下簡(jiǎn)稱(chēng)Redis2.8)中已經(jīng)將這個(gè)部分復(fù)制的思路實(shí)現(xiàn)了抵卫。
那么Redis2.4.16的全量復(fù)制與Redis2.8的部分復(fù)制是如何實(shí)現(xiàn)的呢?如下圖所示,這5個(gè)狀態(tài)是Slave在主從復(fù)制過(guò)程涉及到的幾個(gè)狀態(tài)狮荔,其中REDIS_REPL_NONE是Redis啟動(dòng)時(shí)候默認(rèn)的狀態(tài)。圖1-2所示的四個(gè)狀態(tài)表示站在Master的角度來(lái)看介粘,Slave所處于的狀態(tài)殖氏,因?yàn)镾lave在Master端看來(lái)就是一個(gè)特殊的client(同理Master在Slave端看來(lái)也是一個(gè)特殊的client)。
/* Slave replication state – Slave side */

define REDIS_REPL_NONE 0 /* No active replication */

define REDIS_REPL_CONNECT 1 /* Must connect to Master */

define REDIS_REPL_CONNECTING 2 /* Connecting to Master */

define REDIS_REPL_TRANSFER 3 /* Receiving .rdb from Master */

define REDIS_REPL_CONNECTED 4 /* Connected to Master */

Slave自身的狀態(tài)

define REDIS_REPL_WAIT_BGSAVE_START 3 /* Master waits bgsave to start feeding it */

define REDIS_REPL_WAIT_BGSAVE_END 4 /* Master waits bgsave to start bulk DB transmission */

define REDIS_REPL_SEND_BULK 5 /* Master is sending the bulk DB */

define REDIS_REPL_ONLINE 6 /* bulk DB already transmitted, receive updates */

Master端的Slave狀態(tài)
Redis在接收到“slaveof ip port”命令以后姻采,首先會(huì)將自身的狀態(tài)置為REDIS_REPL_CONNECT雅采,表示需要與自己的Master連接,此時(shí)Slave并沒(méi)有與Master做連接慨亲。Redis每隔100ms會(huì)調(diào)用serverCron()函數(shù)一次婚瓜,每10次serverCron()的調(diào)用會(huì)調(diào)用replicationCron()一次,即每1s會(huì)調(diào)用一次replication()函數(shù)刑棵。在replication()函數(shù)中巴刻,會(huì)檢查Slave的狀態(tài),如果是處于REDIS_REPL_CONNECT狀態(tài)铐望,就會(huì)建立syncWithMaster()的事件處理函數(shù)冈涧,并將Slave的狀態(tài)改成REDIS_REPL_CONNECTING。syncWithMaster()函數(shù)主要是向Master發(fā)送sync命令正蛙,當(dāng)該事件處理函數(shù)被觸發(fā)以后會(huì)將Slave的狀態(tài)改成REDIS_REPL_TRANSFER督弓,表示Slave已經(jīng)準(zhǔn)備就緒要接收Master生成的rdb文件。
回到Master的角色乒验,Master發(fā)現(xiàn)有一個(gè)Slave連接上來(lái)愚隧,如果此時(shí)的Master一個(gè)Slave都沒(méi)有且沒(méi)有后臺(tái)快照進(jìn)程,則啟動(dòng)一個(gè)后臺(tái)進(jìn)程將當(dāng)前內(nèi)存中的數(shù)據(jù)生成一個(gè)rdb文件锻全,同時(shí)將Slave的狀態(tài)置為REDIS_REPL_WAIT_BGSAVE_END狀態(tài)狂塘,表示該Slave等待Master的快照進(jìn)程結(jié)束。在后臺(tái)進(jìn)行生成rdb文件的時(shí)候鳄厌,如果有對(duì)redis的更新命令荞胡,Master會(huì)將這些更新命令存到該Slave的buffer中,如果buffer滿(mǎn)了會(huì)另外開(kāi)辟list來(lái)存儲(chǔ)這些更新命令了嚎。當(dāng)后臺(tái)快照進(jìn)程結(jié)束泪漂,Master會(huì)將該Slave的狀態(tài)改為REDIS_REPL_SEND_BULK,同時(shí)注冊(cè)sendBulkToSlave()事件處理函數(shù)用于將生成的rdb文件傳輸給Slave歪泳。等rdb傳輸結(jié)束以后萝勤,sendBulkToSlave()事件函數(shù)會(huì)被刪除,Slave的狀態(tài)會(huì)被更改為REDIS_REPL_ONLINE呐伞,另外再注冊(cè)sendReplyToClient()事件函數(shù)敌卓,將Master在快照內(nèi)過(guò)程中的所有更新操作(Slave的buffer里存的命令)發(fā)給Slave。
再回到Slave的角色伶氢,當(dāng)Master向Slave傳輸完rdb文件以后趟径,Slave自身會(huì)將狀態(tài)改為REDIS_REPL_CONNECTED瘪吏,表示復(fù)制已完成,處于與Master保持實(shí)時(shí)同步的狀態(tài)舵抹。
上述描述的狀態(tài)轉(zhuǎn)換如圖1-3所示肪虎,由圖中可知,站在Slave角色看惧蛹,當(dāng)出現(xiàn)網(wǎng)絡(luò)中斷的時(shí)候不管Slave本身是處于REDIS_REPL_CONNECTING扇救、REDIS_REPL_REPL_TRANSFER還是REDIS_REPL_CONNECTED,都會(huì)調(diào)用相應(yīng)的處理函數(shù)使Slave進(jìn)入REDIS_REPL_CONNECT狀態(tài)香嗓,這就意味著Slave需要重新向Master發(fā)送sync命令迅腔,重新進(jìn)行一次全量同步過(guò)程。圖中的REDIS_REPL_WAIT_BGSAVE_START狀態(tài)是在Slave連接上Master的時(shí)候(站在Master的角色看)靠娱,當(dāng)時(shí)Master剛好后臺(tái)有快照進(jìn)程且該快照進(jìn)程生成的rdb不適合直接傳給該Slave時(shí)出現(xiàn)的狀態(tài)沧烈,則將Slave的狀態(tài)置為REDIS_REPL_WAIT_BGSAVE_START。如果此時(shí)有快照進(jìn)程且找到了另外的發(fā)起快照進(jìn)程的Slave像云,只需要將另外的Slave的buffer內(nèi)容拷貝到該Slave的buffer中锌雀,然后直接進(jìn)入REDIS_REPL_WAIT_BGSAVE_END狀態(tài)。如果此時(shí)沒(méi)有后臺(tái)快照進(jìn)程迅诬,Slave直接進(jìn)入REDIS_REPL_WAIT_BGSAVE_END狀態(tài)腋逆,同時(shí)啟動(dòng)一個(gè)后臺(tái)快照進(jìn)程。


圖1:Redis-2.4.16主從復(fù)制狀態(tài)轉(zhuǎn)換圖
在上述狀態(tài)轉(zhuǎn)圖中存在的最大問(wèn)題在于任何網(wǎng)絡(luò)閃斷都會(huì)導(dǎo)致Slave與Master重連侈贷,然后重新進(jìn)入快照過(guò)程惩歉,需要花費(fèi)較長(zhǎng)的時(shí)間重新傳輸rdb文件,而Slave在接收完rdb文件以后試圖將rdb文件恢復(fù)到內(nèi)存的過(guò)程中是不能服務(wù)的(除info命令外)俏蛮。所以提供部分復(fù)制至少可以做到在網(wǎng)絡(luò)閃斷且更新命令不太多的情景下能夠盡量的避免全量復(fù)制的方案就顯得尤為重要撑蚌。
慶幸的是Redis2.8中里已經(jīng)能夠做到在網(wǎng)絡(luò)閃斷的情況下,Slave重新連接上Master以后搏屑,僅僅只傳輸閃斷期間的更新命令争涌。在Redis2.8中redisServer結(jié)構(gòu)中增加了一個(gè)成員:
char runid[REDIS_RUN_ID_SIZE+1]; /* ID always different at every exec. /
該runid是由一個(gè)getRandomHexChars()函數(shù)生成的每次不同的一個(gè)唯一標(biāo)識(shí),不同Redis實(shí)例之間該runid是不同的辣恋,同一個(gè)Redis重啟以后第煮,其runid和之前的runid也是不同的。
還增加了比較重要的幾項(xiàng)數(shù)據(jù)成員抑党,如下所示:
char repl_backlog; / Replication backlog for partial syncs /
long long repl_backlog_size; /
Backlog circular buffer size /
long long repl_backlog_histlen; /
Backlog actual data length /
long long repl_backlog_idx; /
Backlog circular buffer current offset /
long long repl_backlog_off; /
Replication offset of first byte in the backlog buffer. /
time_t repl_backlog_time_limit; /
Time without Slaves after the backlog gets released. /
time_t repl_no_Slaves_since; /
We have no Slaves since that time.
Only valid if server.Slaves len is 0. /
Redis2.8增加的數(shù)據(jù)成員
repl_backlog是redis用于存儲(chǔ)更新命令的一塊buffer,在部分復(fù)制的時(shí)候Slave會(huì)請(qǐng)求Master從這塊buffer中獲取閃斷情況下丟失的更新操作撵摆。repl_backlog在redis啟動(dòng)的時(shí)候初始化為NULL底靠,當(dāng)有Slave連接上來(lái)的時(shí)候,會(huì)被指向創(chuàng)建的buffer特铝,默認(rèn)為1024
1024(即1Mb)暑中。repl_backlog_size表示該buffer的大小(默認(rèn)1024
1024壹瘟,即1Mb)。該buffer是作為一個(gè)環(huán)形緩存區(qū)使用的鳄逾,當(dāng)有數(shù)據(jù)超過(guò)buffer的大小以后就會(huì)重新從buffer的頭部開(kāi)始寫(xiě)入稻轨。repl_backlog_idx表示當(dāng)前緩存數(shù)據(jù)的尾部(因?yàn)槭黔h(huán)形buffer)。repl_backlog_off是全局緩存的偏移量雕凹,從開(kāi)始緩存數(shù)據(jù)起一直在增長(zhǎng)殴俱。如果Master一個(gè)Slave都沒(méi)有,則超過(guò)一段時(shí)間以后repl_backlog會(huì)被釋放枚抵,默認(rèn)超時(shí)時(shí)間是1小時(shí)线欲。
Redis2.8的主從復(fù)制如圖1-5所示,Slave如果與Master的連接超時(shí)了汽摹,Slave會(huì)將調(diào)用freeClient(server.Master)把連接關(guān)閉李丰。該freeClient()函數(shù)與2.4版本的相比做了改動(dòng),會(huì)將Master對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)的一些信息存起來(lái)作為cache Master逼泣,其中后續(xù)被用于部分復(fù)制的最重要的兩個(gè)信息一個(gè)是Master runid趴泌,另一個(gè)是reploff。reploff是Slave端接收到Master端傳遞過(guò)來(lái)的命令以后不斷更新記錄的全局偏移量的值拉庶,該值和Master端的repl_backlog_off對(duì)應(yīng)嗜憔,正常情況下reploff<=repl_backlog_off。如果Slave嘗試部分復(fù)制失敗以后砍的,就會(huì)將該cache Master釋放痹筛。
Redis2.8中主從復(fù)制的過(guò)程增加了REDIS_RECIVE_PONG狀態(tài),該狀態(tài)作為試圖與Master同步的時(shí)候先ping一下的一個(gè)中間狀態(tài)廓鞠。當(dāng)ping通以后帚稠,Slave首先會(huì)嘗試部分復(fù)制,從cache Master中拿出Master runid和reploff傳給Master床佳,表示請(qǐng)求部分復(fù)制滋早。第一次的時(shí)候,由于Slave端的cache Master是NULL砌们,所以Slave向Master發(fā)送的runid是“?”杆麸,偏移量是“-1”,當(dāng)Master收到這兩個(gè)變量以后會(huì)將自身的runid和實(shí)際偏移量發(fā)送給Slave浪感,同時(shí)讓Slave發(fā)起一次全量同步昔头。
Slave與Master完全同步以后,maste的更新命令會(huì)被存到repl_backlog中影兽,同時(shí)不斷更新偏移量等相關(guān)變量揭斧。這些更新命令不斷地被發(fā)送到Slave端,Slave也隨之更改自己記錄的偏移量峻堰。當(dāng)期間再次有網(wǎng)絡(luò)斷開(kāi)的情況讹开,Slave會(huì)根據(jù)記錄的runid和reploff向Master請(qǐng)求部分復(fù)制盅视,Master檢查Slave請(qǐng)求的偏移量對(duì)應(yīng)的內(nèi)容是否還在repl_backlog中,即比較repl_backlog_off和Slave傳遞過(guò)來(lái)的reploff的值的差是否小于等于repl_backlog中實(shí)際數(shù)據(jù)的長(zhǎng)度旦万,如果滿(mǎn)足條件則將這部分內(nèi)容發(fā)送給Slave闹击,部分復(fù)制完成。否則讓Slave進(jìn)行全量復(fù)制成艘。
Redis2.8之前的版本沒(méi)有提供部分復(fù)制功能赏半,當(dāng)出現(xiàn)網(wǎng)絡(luò)閃斷的情況會(huì)導(dǎo)致主從之間的全量復(fù)制。Redis2.8增加了部分復(fù)制功能狰腌,在處理網(wǎng)絡(luò)閃斷的情況下是非常有效的除破,這也是出Redis集群之前需要提供的基本保證。默認(rèn)1Mb的repl_backlog在訪問(wèn)量大的情況下可能效果未必理想琼腔,這個(gè)可以通過(guò)更改配置文件中的repl-backlog-size的值實(shí)現(xiàn)repl_backlog的大小的調(diào)整瑰枫。還有repl_backlog在沒(méi)有Slave的情況下過(guò)多久再釋放的時(shí)間閾值也可以通過(guò)配置文件中的repl-backlog-ttl進(jìn)行調(diào)整。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丹莲,一起剝皮案震驚了整個(gè)濱河市光坝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甥材,老刑警劉巖盯另,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異洲赵,居然都是意外死亡鸳惯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)叠萍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芝发,“玉大人,你說(shuō)我怎么就攤上這事苛谷「ňǎ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵腹殿,是天一觀的道長(zhǎng)独悴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)锣尉,這世上最難降的妖魔是什么刻炒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮自沧,結(jié)果婚禮上落蝙,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好筏勒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著旺嬉,像睡著了一般管行。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邪媳,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天捐顷,我揣著相機(jī)與錄音,去河邊找鬼雨效。 笑死迅涮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的徽龟。 我是一名探鬼主播叮姑,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼据悔!你這毒婦竟也來(lái)了传透?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤极颓,失蹤者是張志新(化名)和其女友劉穎朱盐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體菠隆,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兵琳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了骇径。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躯肌。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖既峡,靈堂內(nèi)的尸體忽然破棺而出羡榴,到底是詐尸還是另有隱情,我是刑警寧澤运敢,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布校仑,位于F島的核電站,受9級(jí)特大地震影響传惠,放射性物質(zhì)發(fā)生泄漏迄沫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一卦方、第九天 我趴在偏房一處隱蔽的房頂上張望羊瘩。 院中可真熱鬧,春花似錦、人聲如沸尘吗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)睬捶。三九已至黔宛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間擒贸,已是汗流浹背臀晃。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留介劫,地道東北人徽惋。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像座韵,于是被迫代替她去往敵國(guó)和親险绘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容