深度剖析 Redis 主從架構(gòu)原理
Redis 的主從架構(gòu),其實(shí)就是利用多副本,將一份數(shù)據(jù)同時(shí)保存在多個(gè)實(shí)例上。單個(gè)實(shí)例出現(xiàn)故障后,一般都會(huì)過一段時(shí)間才能恢復(fù),那么其他節(jié)點(diǎn)還是可以提供服務(wù)的。
1. 為什么需要主從架構(gòu)
單點(diǎn)架構(gòu)在Redis中可能會(huì)帶來以下問題:
- 單點(diǎn)故障:Redis單點(diǎn)故障會(huì)導(dǎo)致服務(wù)不可用,造成服務(wù)中斷或者服務(wù)雪崩。高并發(fā)情況下,如果Redis單點(diǎn)出現(xiàn)故障,所有請(qǐng)求都會(huì)受到影響,無法得到有效響應(yīng)。
- 可用性問題:由于Redis單點(diǎn)架構(gòu)沒有備份節(jié)點(diǎn),因此無法在發(fā)生故障時(shí)快速轉(zhuǎn)移服務(wù)以保證系統(tǒng)的持續(xù)可用性。這意味著在單點(diǎn)故障發(fā)生時(shí),服務(wù)可能需要較長(zhǎng)時(shí)間才能恢復(fù)。
- 數(shù)據(jù)丟失風(fēng)險(xiǎn):Redis是內(nèi)存數(shù)據(jù)庫(kù),雖然可以通過RDB和AOF文件進(jìn)行數(shù)據(jù)持久化備份,但在單點(diǎn)架構(gòu)中,如果Redis節(jié)點(diǎn)發(fā)生故障,數(shù)據(jù)恢復(fù)過程可能會(huì)耗時(shí)較長(zhǎng),且存在數(shù)據(jù)丟失的風(fēng)險(xiǎn)。
針對(duì)這些問題,可以采取主從架構(gòu)來提高Redis的可用性和容錯(cuò)性。通過在主節(jié)點(diǎn)上設(shè)置多個(gè)從節(jié)點(diǎn),可以實(shí)現(xiàn)數(shù)據(jù)的復(fù)制和故障轉(zhuǎn)移,從而降低單點(diǎn)故障的影響,提高系統(tǒng)的穩(wěn)定性和可用性。
2. 主從架構(gòu)原理
主從架構(gòu),事件就是數(shù)據(jù)可以在多個(gè)實(shí)例上進(jìn)行復(fù)制,當(dāng)主節(jié)點(diǎn)出現(xiàn)故障時(shí),從節(jié)點(diǎn)可以接管服務(wù),從而實(shí)現(xiàn)快速故障轉(zhuǎn)移,保證服務(wù)的持續(xù)可用性。此外,主從架構(gòu)還可以提高系統(tǒng)的并發(fā)能力,因?yàn)槎鄠€(gè)節(jié)點(diǎn)可以同時(shí)處理請(qǐng)求。這樣,在主節(jié)點(diǎn)故障時(shí),從節(jié)點(diǎn)可以立即接管服務(wù),避免了單點(diǎn)故障導(dǎo)致的服務(wù)中斷或雪崩效應(yīng)。
3. 主從架構(gòu)拓?fù)鋱D
在Redis中,主從架構(gòu)的確實(shí)是一種主從庫(kù)模式,其中主節(jié)點(diǎn)負(fù)責(zé)處理寫操作并將數(shù)據(jù)同步到從節(jié)點(diǎn),從節(jié)點(diǎn)則負(fù)責(zé)處理讀操作。這種主從庫(kù)模式可以根據(jù)節(jié)點(diǎn)之間的拓?fù)浣Y(jié)構(gòu)分為以下三種類型:
- 單主單從結(jié)構(gòu)(Single Master-Single Slave):一個(gè)主節(jié)點(diǎn),一個(gè)從節(jié)點(diǎn),主節(jié)點(diǎn)可讀可寫,從節(jié)點(diǎn)只接收讀請(qǐng)求。常用于主節(jié)點(diǎn)出現(xiàn)故障時(shí),從節(jié)點(diǎn)能夠快速頂上。
圖片
- 單主多從結(jié)構(gòu)(Single Master-Multiple Slaves):一個(gè)主節(jié)點(diǎn),多個(gè)從節(jié)點(diǎn),對(duì)于讀命令較大的場(chǎng)景,可以把讀命令分?jǐn)偟蕉鄠€(gè)從節(jié)點(diǎn)。
圖片
- 樹狀主從結(jié)構(gòu):一個(gè)主節(jié)點(diǎn),多個(gè)從節(jié)點(diǎn),其中一個(gè)從節(jié)點(diǎn)作為中間層,既可以復(fù)制主節(jié)點(diǎn),又可以當(dāng)做其他從節(jié)點(diǎn)復(fù)制的主節(jié)點(diǎn)。有效降低主節(jié)點(diǎn)負(fù)載和需要傳送給從節(jié)點(diǎn)的數(shù)據(jù)量。
圖片
4. 主從數(shù)據(jù)同步原理
4.1.全量同步
主從第一次建立連接時(shí),會(huì)執(zhí)行全量同步,將master節(jié)點(diǎn)的所有數(shù)據(jù)都拷貝給slave節(jié)點(diǎn),流程:
圖片
思考:master如何得知salve是第一次來連接呢?
有幾個(gè)概念,可以作為判斷依據(jù):
- Replication Id:簡(jiǎn)稱replid,是數(shù)據(jù)集的標(biāo)記,id一致則說明是同一數(shù)據(jù)集。每一個(gè)master都有唯一的replid,slave則會(huì)繼承master節(jié)點(diǎn)的replid
- offset:偏移量,隨著記錄在repl_baklog中的數(shù)據(jù)增多而逐漸增大。slave完成同步時(shí)也會(huì)記錄當(dāng)前同步的offset。如果slave的offset小于master的offset,說明slave數(shù)據(jù)落后于master,需要更新。
slave數(shù)據(jù)同步,必須向master聲明自己的replication id 和offset,master才可以判斷到底需要同步哪些數(shù)據(jù)。
slave原本也是一個(gè)master,有自己的replid和offset,當(dāng)?shù)谝淮巫兂蓅lave,與master建立連接時(shí),發(fā)送的replid和offset是自己的replid和offset。
master判斷發(fā)現(xiàn)slave發(fā)送來的replid與自己的不一致,說明這是一個(gè)全新的slave,就知道要做全量同步了
master會(huì)將自己的replid和offset都發(fā)送給這個(gè)slave,slave保存這些信息。以后slave的replid就與master一致了。
因此,master判斷一個(gè)節(jié)點(diǎn)是否是第一次同步的依據(jù),就是看replid是否一致。
圖片
完整流程描述:
- slave節(jié)點(diǎn)請(qǐng)求增量同步
- master節(jié)點(diǎn)判斷replid,發(fā)現(xiàn)不一致,拒絕增量同步
- master將完整內(nèi)存數(shù)據(jù)生成RDB,發(fā)送RDB到slave
- slave清空本地?cái)?shù)據(jù),加載master的RDB
- master將RDB期間的命令記錄在repl_baklog,并持續(xù)將log中的命令發(fā)送給slave
- slave執(zhí)行接收到的命令,保持與master之間的同步
4.2.增量同步
全量同步需要先做RDB,然后將RDB文件通過網(wǎng)絡(luò)傳輸個(gè)slave,成本太高了。因此除了第一次做全量同步,其它大多數(shù)時(shí)候slave與master都是做增量同步。
思考:什么是增量同步?
增量同步是只更新slave與master存在差異的部分?jǐn)?shù)據(jù)。
圖片
思考:master怎么知道slave與自己的數(shù)據(jù)差異在哪里呢?
5.repl_backlog原理
這就要說到全量同步時(shí)的repl_baklog文件了。
這個(gè)文件是一個(gè)固定大小的數(shù)組,只不過數(shù)組是環(huán)形,也就是說角標(biāo)到達(dá)數(shù)組末尾后,會(huì)再次從0開始讀寫,這樣數(shù)組頭部的數(shù)據(jù)就會(huì)被覆蓋。
- repl_baklog中會(huì)記錄Redis處理過的命令日志及offset,包括master當(dāng)前的offset,和slave已經(jīng)拷貝到的offset:
圖片
slave與master的offset之間的差異,就是salve需要增量拷貝的數(shù)據(jù)了。
隨著不斷有數(shù)據(jù)寫入,master的offset逐漸變大,slave也不斷的拷貝,追趕master的offset:
圖片
直到數(shù)組被填滿:
圖片
此時(shí),如果有新的數(shù)據(jù)寫入,就會(huì)覆蓋數(shù)組中的舊數(shù)據(jù)。不過,舊的數(shù)據(jù)只要是綠色的,說明是已經(jīng)被同步到slave的數(shù)據(jù),即便被覆蓋了也沒什么影響。因?yàn)槲赐降膬H僅是紅色部分。
但是,如果slave出現(xiàn)網(wǎng)絡(luò)阻塞,導(dǎo)致master的offset遠(yuǎn)遠(yuǎn)超過了slave的offset:
圖片
如果master繼續(xù)寫入新數(shù)據(jù),其offset就會(huì)覆蓋舊的數(shù)據(jù),直到將slave現(xiàn)在的offset也覆蓋:
圖片
棕色框中的紅色部分,就是尚未同步,但是卻已經(jīng)被覆蓋的數(shù)據(jù)。此時(shí)如果slave恢復(fù),需要同步,卻發(fā)現(xiàn)自己的offset都沒有了,無法完成增量同步了,只能做全量同步。
注意:repl_baklog大小有上限,寫滿后會(huì)覆蓋最早數(shù)據(jù),如果slave斷開時(shí)間過久,導(dǎo)致尚未備份的數(shù)據(jù)被覆蓋,否則無法基于log做增量同步,只能再次全量同步。