淺析Redis中的集群主從復(fù)制原理

本篇文章帶大家深入理解下redis集群主從復(fù)制原理,希望對(duì)大家有所幫助!

淺析Redis中的集群主從復(fù)制原理

一、首先思考一個(gè)問(wèn)題,為什么redis性能這么高還需要分布式方案?

1、實(shí)現(xiàn)更高性能:高并發(fā)應(yīng)用,單機(jī)性能會(huì)有影響,需要更多redis服務(wù)器分擔(dān)壓力,實(shí)現(xiàn)負(fù)載均衡

2、實(shí)現(xiàn)高可用:如果單機(jī),防止宕機(jī)/硬件故障

3、實(shí)現(xiàn)可擴(kuò)展:?jiǎn)螜C(jī)內(nèi)存和硬件有限制,實(shí)現(xiàn)橫向擴(kuò)展

冗余或者分片存儲(chǔ)實(shí)現(xiàn)如上特性。

二、主從復(fù)制-replication配置

kafka,mysql,rocketmq一樣,redis支持集群部署,集群節(jié)點(diǎn)有master和slave之分,主節(jié)點(diǎn)是master,從節(jié)點(diǎn)是slave(最新叫副本replica).slave會(huì)通過(guò)復(fù)制機(jī)制,從master同步最新的數(shù)據(jù)。Redis提供了非常方便的命令開(kāi)啟主從復(fù)制。【相關(guān)推薦:Redis視頻教程

如何配置開(kāi)啟主從復(fù)制?

以本機(jī)搭建偽集群為例,6379端口是從節(jié)點(diǎn),6378作為主節(jié)點(diǎn)。

1、從節(jié)點(diǎn)redis.conf配置 replicaof masterip masterport 從節(jié)點(diǎn)啟動(dòng)后,自動(dòng)連接到master節(jié)點(diǎn),開(kāi)始同步數(shù)據(jù).

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

如果換了新的master節(jié)點(diǎn),這個(gè)配置會(huì)被重寫(xiě)。

2、或者在redis-server程序啟動(dòng)時(shí)候指定

./redis-server?--replicaof?masterip?masterport

3、或者登錄客戶(hù)端,執(zhí)行如下命令

slaveof?masterip?masterport

注意這種方式是運(yùn)行過(guò)程中修改,可以實(shí)現(xiàn)故障轉(zhuǎn)移

注意: 一個(gè)從節(jié)點(diǎn)也可以是其他節(jié)點(diǎn)的主節(jié)點(diǎn),形成級(jí)聯(lián)復(fù)制的關(guān)系。但是其他節(jié)點(diǎn)也是從頂層主節(jié)點(diǎn)同步數(shù)據(jù)。

配置好集群后,通過(guò)info replication查看集群狀態(tài)

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

通過(guò)role命令,可以查看節(jié)點(diǎn)在集群中的角色信息

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

注意從節(jié)點(diǎn)是只讀的。寫(xiě)命令會(huì)報(bào)錯(cuò)。?

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

slave如何退出集群? 可以執(zhí)行如下命令:?

slaveof?no?one

三、主從復(fù)制的流程

1、首先是副本-replica加入集群?

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

2、與master建立連接,通過(guò)定時(shí)器定時(shí)檢查是否要從主節(jié)點(diǎn)同步數(shù)據(jù)

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

源碼說(shuō)明:

//每1s執(zhí)行這個(gè)方法 void?replicationCron(void)?{ ????... ????//檢查是否需要連接到master?如果是REPL_STATE_CONNECT狀態(tài),必須連接到master ????//#define?REPL_STATE_CONNECT?1??Must?connect?to?master? ????if?(server.repl_state?==?REPL_STATE_CONNECT)?{ ????????serverLog(LL_NOTICE,"Connecting?to?MASTER?%s:%d", ????????????server.masterhost,?server.masterport); ????????//和master創(chuàng)建連接???? ????????if?(connectWithMaster()?==?C_OK)?{ ????????????serverLog(LL_NOTICE,"MASTER??REPLICA?sync?started"); ????????} ????} ???? ????//發(fā)送ping命令給slave? ????if?((replication_cron_loops?%?server.repl_ping_slave_period)?==?0?&& ????????listLength(server.slaves)) ????{ ????????/*?Note?that?we?don't?send?the?PING?if?the?clients?are?paused?during ?????????*?a?Redis?Cluster?manual?failover:?the?PING?we?send?will?otherwise ?????????*?alter?the?replication?offsets?of?master?and?slave,?and?will?no?longer ?????????*?match?the?one?stored?into?'mf_master_offset'?state.?*/ ????????int?manual_failover_in_progress?= ????????????server.cluster_enabled?&& ????????????server.cluster->mf_end?&& ????????????clientsArePaused();  ????????if?(!manual_failover_in_progress)?{ ????????????ping_argv[0]?=?createStringObject("PING",4); ????????????replicationFeedSlaves(server.slaves,?server.slaveseldb, ????????????????ping_argv,?1); ????????????decrRefCount(ping_argv[0]); ????????} ????} ???? ????//發(fā)送換行符到所有slave,告訴slave等待接收rdb文件 ????listRewind(server.slaves,&li); ????while((ln?=?listNext(&li)))?{ ????????client?*slave?=?ln->value;  ????????int?is_presync?= ????????????(slave->replstate?==?SLAVE_STATE_WAIT_BGSAVE_START?|| ????????????(slave->replstate?==?SLAVE_STATE_WAIT_BGSAVE_END?&& ?????????????server.rdb_child_type?!=?RDB_CHILD_TYPE_SOCKET));  ????????if?(is_presync)?{ ????????????if?(write(slave->fd,?"n",?1)?==?-1)?{ ????????????????/*?Don't?worry?about?socket?errors,?it's?just?a?ping.?*/ ????????????} ????????} ????} ????... }

3、全量復(fù)制流程-支持無(wú)盤(pán)復(fù)制或者rdb持久化復(fù)制?

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

當(dāng)slave連接到master后,使用psync(以前是sync命令,它不允許部分重新同步,所以現(xiàn)在改用PSYNC)命令初始化復(fù)制,將主節(jié)點(diǎn)replication id和處理過(guò)最大offset發(fā)送到master。

master節(jié)點(diǎn)擁有如下兩個(gè)屬性,一個(gè)replication id(標(biāo)志實(shí)例),一個(gè)offset(標(biāo)志寫(xiě)入從節(jié)點(diǎn)的stream)?

Replication?ID,?offset

如果主節(jié)點(diǎn)緩沖區(qū)中沒(méi)有足夠的積壓工作,或者如果復(fù)制副本引用的是不再已知的歷史記錄(復(fù)制ID),則會(huì)發(fā)生完全重新同步?

源碼說(shuō)明:

????//沒(méi)有在rdb進(jìn)程,沒(méi)有aof重寫(xiě)進(jìn)程 ????if?(server.rdb_child_pid?==?-1?&&?server.aof_child_pid?==?-1)?{ ????????time_t?idle,?max_idle?=?0; ????????int?slaves_waiting?=?0; ????????int?mincapa?=?-1; ????????listNode?*ln; ????????listIter?li;  ????????listRewind(server.slaves,&li); ????????while((ln?=?listNext(&li)))?{ ????????????client?*slave?=?ln->value; ????????????//判斷slave是否是等待bgsave狀態(tài) ????????????if?(slave->replstate?==?SLAVE_STATE_WAIT_BGSAVE_START)?{ ????????????//多久沒(méi)有發(fā)送心跳或查詢(xún)數(shù)據(jù)了?空閑時(shí)間間隔 ????????????????idle?=?server.unixtime?-?slave->lastinteraction; ????????????????if?(idle?>?max_idle)?max_idle?=?idle; ????????????????slaves_waiting++; ????????????????mincapa?=?(mincapa?==?-1)???slave->slave_capa?: ????????????????????????????????????????????(mincapa?&?slave->slave_capa); ????????????} ????????}  ????????if?(slaves_waiting?&& ????????????(!server.repl_diskless_sync?|| ?????????????max_idle?>?server.repl_diskless_sync_delay)) ????????{ ????????????/*?Start?the?BGSAVE.?The?called?function?may?start?a ?????????????*?BGSAVE?with?socket?target?or?disk?target?depending?on?the ?????????????*?configuration?and?slaves?capabilities.?*/ ?????????????//bgsave?rdb生成 ????????????startBgsaveForReplication(mincapa); ????????} ????}

復(fù)制過(guò)程中,slave狀態(tài)轉(zhuǎn)換流程。

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

4、命令傳播階段,執(zhí)行完全量同步后,主從會(huì)進(jìn)行命令傳播實(shí)現(xiàn)數(shù)據(jù)一致。

淺析Redis中的集群主從復(fù)制原理淺析Redis中的集群主從復(fù)制原理

四、復(fù)制id理解

每次實(shí)例作為主實(shí)例從頭開(kāi)始重新啟動(dòng),或者將復(fù)制副本提升為主實(shí)例,都會(huì)為此實(shí)例生成一個(gè)新的復(fù)制ID。如果兩個(gè)replica的復(fù)制id相同,則他們可能在不同的時(shí)間,有相同的數(shù)據(jù),對(duì)于保存最新數(shù)據(jù)集的給定歷史記錄(復(fù)制ID),偏移量作為一個(gè)邏輯時(shí)間來(lái)理解。需要通過(guò)Replication ID, offset兩個(gè)數(shù)據(jù)來(lái)判斷。用來(lái)判斷從節(jié)點(diǎn)同步數(shù)據(jù)到哪了。

五、主從復(fù)制常見(jiàn)問(wèn)題

1、slave本身有數(shù)據(jù),會(huì)怎么樣?

slave先刪除自身的數(shù)據(jù),再用rdb文件加載。

2、生成rdb文件的過(guò)程中,客戶(hù)端寫(xiě)命令怎么處理?

保存到內(nèi)存緩存中,rdb發(fā)送完成后發(fā)送到slave。?

3、Redis復(fù)制如何處理key過(guò)期的??

1、副本不會(huì)使key過(guò)期,而是等待主機(jī)使key過(guò)期。當(dāng)主機(jī)使key過(guò)期(或由于LRU而將其逐出)時(shí),它將合成一個(gè)DEL命令,該命令將傳輸?shù)剿懈北尽?/p>

2、但是,由于主機(jī)驅(qū)動(dòng)的expire,有時(shí)副本可能仍然具有邏輯上已過(guò)期的內(nèi)存密鑰,因?yàn)橹鞣?wù)器無(wú)法及時(shí)提供DEL命令。為了處理這個(gè)問(wèn)題,副本使用它的邏輯時(shí)鐘來(lái)報(bào)告一個(gè)key不存在,只用于不違反數(shù)據(jù)集一致性的讀取操作(因?yàn)閬?lái)自主服務(wù)器的新命令將到達(dá))

3、在lua腳本執(zhí)行期間,不執(zhí)行密鑰過(guò)期。當(dāng)Lua腳本運(yùn)行時(shí),從概念上講,主節(jié)點(diǎn)中的時(shí)間是凍結(jié)的,因此給定的鍵在腳本運(yùn)行的所有時(shí)間內(nèi)都將存在或不存在。這可以防止key在腳本中間過(guò)期,并且需要key才能以保證在數(shù)據(jù)集中具有相同效果的方式將相同的腳本發(fā)送到副本。

一旦復(fù)制副本升級(jí)為主副本,它將開(kāi)始獨(dú)立地使key過(guò)期,并且不需要舊主副本的任何幫助。?

六、主從復(fù)制總結(jié)

1、解決了數(shù)據(jù)備份的問(wèn)題,但是rdb文件大,傳輸大文件,恢復(fù)時(shí)間也長(zhǎng)

2、如果master異常,需要手工將replica選舉為master

3、1主多從,1主1從的情況下,還是存在單點(diǎn)問(wèn)題?

4、Redis版本2.8.18后支持無(wú)盤(pán)復(fù)制,性能更高。

七、復(fù)制說(shuō)明

1、默認(rèn)用異步復(fù)制,通過(guò)異步確認(rèn)同步的命令數(shù)量

2、1個(gè)master可以有多個(gè)副本

3、副本也可以有自己的副本,從redis4.0開(kāi)始,副本都會(huì)從主節(jié)點(diǎn)接收完全相同的復(fù)制流

4、復(fù)制既可以用于可擴(kuò)展性,也可以用于只讀查詢(xún)的多個(gè)副本

更多編程相關(guān)知識(shí),請(qǐng)?jiān)L問(wèn):Redis視頻教程!!

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊7 分享