Redis主從復(fù)制

主從復(fù)制概述

有了 RDB 和 AOF 再也不怕宕機(jī)丟失數(shù)據(jù)了,但是 Redis 實(shí)例宕機(jī)了怎么實(shí)現(xiàn)高可用呢?既然一臺(tái)宕機(jī)了無(wú)法提供服務(wù),那多臺(tái)呢?是不是就可以解決了。Redis 提供了主從模式,通過(guò)主從復(fù)制,將數(shù)據(jù)冗余一份復(fù)制到其他 Redis 服務(wù)器。

前者稱為主節(jié)點(diǎn) (master),后者稱為從節(jié)點(diǎn) (slave);數(shù)據(jù)的復(fù)制是單向的,只能由主節(jié)點(diǎn)到從節(jié)點(diǎn)。

為了保證副本數(shù)據(jù)的一致性,主從架構(gòu)采用了讀寫分離的方式。

  • 讀操作:主、從庫(kù)都可以執(zhí)行;
  • 寫操作:主庫(kù)先執(zhí)行,之后將寫操作同步到從庫(kù);

為何要采用讀寫分離的方式?

我們可以假設(shè)主從庫(kù)都可以執(zhí)行寫指令,假如對(duì)同一份數(shù)據(jù)分別修改了多次,每次修改發(fā)送到不同的主從實(shí)例上,就導(dǎo)致是實(shí)例的副本數(shù)據(jù)不一致了。

搭建主從復(fù)制

主從復(fù)制的開(kāi)啟,完全是在從節(jié)點(diǎn)發(fā)起的,不需要我們?cè)谥鞴?jié)點(diǎn)做任何事情。

可以通過(guò) replicaof(Redis 5.0 之前使用 slaveof)命令形成主庫(kù)和從庫(kù)的關(guān)系。

在從節(jié)點(diǎn)開(kāi)啟主從復(fù)制,有 3 種方式:

1、配置文件
在從服務(wù)器的配置文件中加入 replicaof <masterip> <masterport>

2、啟動(dòng)命令
redis-server 啟動(dòng)命令后面加入 --replicaof <masterip> <masterport>

3、客戶端命令
啟動(dòng)多個(gè) Redis 實(shí)例后,直接通過(guò)客戶端執(zhí)行命令:replicaof <masterip> <masterport>,則該 Redis 實(shí)例成為從節(jié)點(diǎn)。

主從復(fù)制原理

主從庫(kù)模式一旦采用了讀寫分離,所有數(shù)據(jù)的寫操作只會(huì)在主庫(kù)上進(jìn)行,不用協(xié)調(diào)三個(gè)實(shí)例。

主庫(kù)有了最新的數(shù)據(jù)后,會(huì)同步給從庫(kù),這樣,主從庫(kù)的數(shù)據(jù)就是一致的。

同步分為三種情況:

1、第一次主從庫(kù)全量復(fù)制;
2、主從正常運(yùn)行期間的同步;
3、主從庫(kù)間網(wǎng)絡(luò)斷開(kāi)重連同步

主從庫(kù)第一次全量復(fù)制

主從庫(kù)第一次復(fù)制過(guò)程大體可以分為 3 個(gè)階段:連接建立階段(即準(zhǔn)備階段)、主庫(kù)同步數(shù)據(jù)到從庫(kù)階段、發(fā)送同步期間新寫命令到從庫(kù)階段;

建立連接

該階段的主要作用是在主從節(jié)點(diǎn)之間建立連接,為數(shù)據(jù)全量同步做好準(zhǔn)備。從庫(kù)會(huì)和主庫(kù)建立連接,從庫(kù)執(zhí)行 replicaof 并發(fā)送 psync 命令并告訴主庫(kù)即將進(jìn)行同步,主庫(kù)確認(rèn)回復(fù)后,主從庫(kù)間就開(kāi)始同步了。

從庫(kù)怎么知道主庫(kù)信息并建立連接的呢?

在從節(jié)點(diǎn)的配置文件中的 replicaof 配置項(xiàng)中配置了主節(jié)點(diǎn)的 IP 和 port 后,從節(jié)點(diǎn)就知道自己要和那個(gè)主節(jié)點(diǎn)進(jìn)行連接了。

從節(jié)點(diǎn)內(nèi)部維護(hù)了兩個(gè)字段,masterhost 和 masterport,用于存儲(chǔ)主節(jié)點(diǎn)的 IP 和 port 信息。

從庫(kù)執(zhí)行 replicaof 并發(fā)送 psync 命令,表示要執(zhí)行數(shù)據(jù)同步,主庫(kù)收到命令后根據(jù)參數(shù)啟動(dòng)復(fù)制。命令包含了主庫(kù)的 runID 和 復(fù)制進(jìn)度 offset 兩個(gè)參數(shù)。

  • runID:每個(gè) Redis 實(shí)例啟動(dòng)都會(huì)自動(dòng)生成一個(gè) 唯一標(biāo)識(shí) ID,第一次主從復(fù)制,還不知道主庫(kù) runID,參數(shù)設(shè)置為 「?」。
  • offset:第一次復(fù)制設(shè)置為 -1,表示第一次復(fù)制,記錄復(fù)制進(jìn)度偏移量。

主庫(kù)收到 psync 命令后,會(huì)用 FULLRESYNC 響應(yīng)命令帶上兩個(gè)參數(shù):主庫(kù) runID 和主庫(kù)目前的復(fù)制進(jìn)度 offset,返回給從庫(kù)。從庫(kù)收到響應(yīng)后,會(huì)記錄下這兩個(gè)參數(shù)。

FULLRESYNC 響應(yīng)表示第一次復(fù)制采用的全量復(fù)制,也就是說(shuō),主庫(kù)會(huì)把當(dāng)前所有的數(shù)據(jù)都復(fù)制給從庫(kù)。

主庫(kù)同步數(shù)據(jù)給從庫(kù)

第二階段

master 執(zhí)行 bgsave命令生成 RDB 文件,并將文件發(fā)送給從庫(kù),同時(shí)主庫(kù)為每一個(gè) slave 開(kāi)辟一塊 replication buffer 緩沖區(qū)記錄從生成 RDB 文件開(kāi)始收到的所有寫命令。

從庫(kù)收到 RDB 文件后保存到磁盤,并清空當(dāng)前數(shù)據(jù)庫(kù)的數(shù)據(jù),再加載 RDB 文件數(shù)據(jù)到內(nèi)存中。

發(fā)送新寫命令到從庫(kù)

第三階段

從節(jié)點(diǎn)加載 RDB 完成后,主節(jié)點(diǎn)將 replication buffer 緩沖區(qū)的數(shù)據(jù)發(fā)送到從節(jié)點(diǎn),Slave 接收并執(zhí)行,從節(jié)點(diǎn)同步至主節(jié)點(diǎn)相同的狀態(tài)。

主庫(kù)將數(shù)據(jù)同步到從庫(kù)過(guò)程中,可以正常接受請(qǐng)求么?

主庫(kù)不會(huì)被阻塞,在生成 RDB 文件之后的寫操作并沒(méi)有記錄到剛剛的 RDB 文件中,為了保證主從庫(kù)數(shù)據(jù)的一致性,所以主庫(kù)會(huì)在內(nèi)存中使用一個(gè)叫 replication buffer 記錄 RDB 文件生成后的所有寫操作。

replication buffer 是一個(gè)在 master 端上創(chuàng)建的緩沖區(qū),存放的數(shù)據(jù)是下面三個(gè)時(shí)間內(nèi)所有的 master 數(shù)據(jù)寫操作。

1)master 執(zhí)行 bgsave 產(chǎn)生 RDB 的期間的寫操作;

2)master 發(fā)送 rdb 到 slave 網(wǎng)絡(luò)傳輸期間的寫操作;

3)slave load rdb 文件把數(shù)據(jù)恢復(fù)到內(nèi)存的期間的寫操作。

主從正常運(yùn)行期間的同步

當(dāng)主從庫(kù)完成了全量復(fù)制,它們之間就會(huì)一直維護(hù)一個(gè)網(wǎng)絡(luò)連接,主庫(kù)會(huì)通過(guò)這個(gè)連接將后續(xù)陸續(xù)收到的命令操作再同步給從庫(kù),這個(gè)過(guò)程也稱為基于長(zhǎng)連接的命令傳播,使用長(zhǎng)連接的目的就是避免頻繁建立連接導(dǎo)致的開(kāi)銷。

在命令傳播階段,除了發(fā)送寫命令,主從節(jié)點(diǎn)還維持著心跳機(jī)制:PING 和 REPLCONF ACK。

主->從:PING
每隔指定的時(shí)間,主節(jié)點(diǎn)會(huì)向從節(jié)點(diǎn)發(fā)送 PING 命令,這個(gè) PING 命令的作用,主要是為了讓從節(jié)點(diǎn)進(jìn)行超時(shí)判斷。

從->主:REPLCONF ACK
在命令傳播階段,從服務(wù)器默認(rèn)會(huì)以每秒一次的頻率,向主服務(wù)器發(fā)送命令:

主從庫(kù)間網(wǎng)絡(luò)斷開(kāi)重連同步

在 Redis 2.8 之前,如果主從庫(kù)在命令傳播時(shí)出現(xiàn)了網(wǎng)絡(luò)閃斷,那么,從庫(kù)就會(huì)和主庫(kù)重新進(jìn)行一次全量復(fù)制,開(kāi)銷非常大。

從 Redis 2.8 開(kāi)始,網(wǎng)絡(luò)斷了之后,主從庫(kù)會(huì)采用增量復(fù)制的方式繼續(xù)同步。

增量復(fù)制:用于網(wǎng)絡(luò)中斷等情況后的復(fù)制,只將中斷期間主節(jié)點(diǎn)執(zhí)行的寫命令發(fā)送給從節(jié)點(diǎn),與全量復(fù)制相比更加高效。

repl_backlog_buffer

斷開(kāi)重連增量復(fù)制的實(shí)現(xiàn)奧秘就是 repl_backlog_buffer 緩沖區(qū),不管在什么時(shí)候 master 都會(huì)將寫指令操作記錄在 repl_backlog_buffer 中,因?yàn)閮?nèi)存有限, repl_backlog_buffer 是一個(gè)定長(zhǎng)的環(huán)形數(shù)組,如果數(shù)組內(nèi)容滿了,就會(huì)從頭開(kāi)始覆蓋前面的內(nèi)容。

master 使用 master_repl_offset記錄自己寫到的位置偏移量,slave 則使用 slave_repl_offset記錄已經(jīng)讀取到的偏移量。

master 收到寫操作,偏移量則會(huì)增加。從庫(kù)持續(xù)執(zhí)行同步的寫指令后,在 repl_backlog_buffer 的已復(fù)制的偏移量 slave_repl_offset 也在不斷增加。

正常情況下,這兩個(gè)偏移量基本相等。在網(wǎng)絡(luò)斷連階段,主庫(kù)可能會(huì)收到新的寫操作命令,所以 master_repl_offset會(huì)大于 slave_repl_offset。

當(dāng)主從斷開(kāi)重連后,slave 會(huì)先發(fā)送 psync 命令給 master,同時(shí)將自己的 runID,slave_repl_offset發(fā)送給 master。

master 只需要把 master_repl_offset與 slave_repl_offset之間的命令同步給從庫(kù)即可。

增量復(fù)制執(zhí)行流程如下圖:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容