Redis主從復(fù)制原理和復(fù)制方式那點(diǎn)事!
通過學(xué)習(xí)我們知道通過持久化技術(shù)讓服務(wù)器重啟的情況下盡可能少或者不會(huì)丟失數(shù)據(jù)。
但是問題在于持久化的數(shù)據(jù)在單一的服務(wù)器上,萬一服務(wù)器的硬盤出現(xiàn)了故障,那就可能數(shù)據(jù)就真的沒了!
而在Redis中主從復(fù)制,是指將一臺(tái)Redis服務(wù)器的數(shù)據(jù),復(fù)制到其他的Redis服務(wù)器(數(shù)據(jù)備份了)。被復(fù)制的服務(wù)器稱為主服務(wù)器(master),對(duì)主服務(wù)器進(jìn)行復(fù)制操作的為從服務(wù)器(slave)。
?? 要注意的是數(shù)據(jù)的復(fù)制是單向的,只能由主節(jié)點(diǎn)到從節(jié)點(diǎn)!
主從服務(wù)器庫之間采用的是讀寫分離的方式
? 讀操作:主庫、從庫都可以接收讀操作
? 寫操作:首先到主庫執(zhí)行寫,然后,主庫將寫操作同步給從庫
圖片
主從復(fù)制的好處
? 數(shù)據(jù)冗余 :實(shí)現(xiàn)數(shù)據(jù)的熱備份
? 故障恢復(fù) :避免單點(diǎn)故障帶來的服務(wù)不可用,可以由從節(jié)點(diǎn)提供服務(wù),實(shí)現(xiàn)快速的故障恢復(fù)
? 讀寫分離 :在主從復(fù)制的基礎(chǔ)上,配合讀寫分離,可以由主節(jié)點(diǎn)提供寫服務(wù),由從節(jié)點(diǎn)提供讀服務(wù),可分擔(dān)服務(wù)器負(fù)載;尤其是在寫少讀多的場景下,通過多個(gè)從節(jié)點(diǎn)分擔(dān)讀負(fù)載,可以大大提高Redis服務(wù)器的并發(fā)量
? 高可用基礎(chǔ) :是哨兵機(jī)制和集群實(shí)現(xiàn)的基礎(chǔ)
圖片
今天小許將分享Redis高可用知識(shí)點(diǎn)之【Redis主從復(fù)制】,可能你在其他地方看過,相信你跟著小許思路,能幫你重新回憶一遍、不會(huì)的同學(xué)好好學(xué)一波!
內(nèi)容比較多,在地鐵上看文章的你可以先關(guān)注、收藏一下,用電腦看舒服!
圖片
實(shí)現(xiàn)原理
進(jìn)行復(fù)制之前我們要確定的是誰是主和從服務(wù)器,我們將在從從服務(wù)器上使用 slaveof命令形成主從關(guān)系,命令如下:
redis 5.0之前使用 salveof 命令(salveof <master IP 地址> <master 端口號(hào)>)
redis 5.0之后使用 replicaof 命令(replicaof <master IP 地址> <master 端口號(hào)>)
文章后續(xù)將用 master 表示主服務(wù)器 , slave 表示從服務(wù)器。
Redis服務(wù)器執(zhí)行上述命令的成為了從服務(wù)器slave,我們看下進(jìn)行復(fù)制涉及了哪些流程,然后一個(gè)個(gè)看看每個(gè)流程具體干了什么,了解Master和Slave復(fù)制的實(shí)現(xiàn)!
圖片
設(shè)置master 地址和端口
在準(zhǔn)備成為slave的Redis服務(wù)器上執(zhí)行下面命令:
127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK
salve服務(wù)器執(zhí)行salveof命令【異步命令】的目的是,將給定的master服務(wù)器IP地址127.0.0.1和端口6379,保存到從服務(wù)器的masterhost屬性和masterport屬性里面。
redisServer 結(jié)構(gòu)體的字段太多了,這里只展示復(fù)制是master的一部分,還有slave的部分屬性就不展示了!
//salve的服務(wù)器結(jié)構(gòu)體值
struct redisServer{
//...
/* Replication (slave) */
// 驗(yàn)證信息
char *masterauth;
// 命令執(zhí)行成功后值 127.0.0.1
char *masterhost;
// 6379
int masterport;
//...
};
建立socket連接
slave將根據(jù)命令(slave或replicaof)設(shè)置的IP地址和端口,創(chuàng)建連向master的套接字(socket)連接。
socket連接到master之后,會(huì)注冊(cè)一個(gè)文件事件【syncWithMaster】,接收后續(xù)RDB文件、實(shí)時(shí)寫命令,當(dāng)然在連接建立之后,master也會(huì)創(chuàng)建相應(yīng)的客戶端狀態(tài)啦!
圖片
發(fā)送 ping 命令
slave成為mater的客戶端之后先發(fā)送PING命令,主要作用如下:
1. 檢查salve和master的套接字讀寫狀態(tài)
2. 檢查master是否能正常處理命令
slave收到PONG回復(fù)后才會(huì)繼續(xù)執(zhí)行后續(xù)步驟!
身份驗(yàn)證
客戶端如果開啟了密碼保護(hù)的話,在每次連接 Redis 服務(wù)器之后,就要使用 AUTH 命令解鎖,解鎖之后才能使用其他 Redis 命令。
也就是說如果slave設(shè)置了 masterauth 屬性,那么將會(huì)向master發(fā)送一條AUTH命令進(jìn)行身份驗(yàn)證,目的是檢驗(yàn)給定的密碼 password 和master配置文件中requirepass 項(xiàng)的值是否相符。
向master發(fā)送監(jiān)聽端口
slave身份驗(yàn)證之后,發(fā)送執(zhí)行以下命令向mater發(fā)送監(jiān)聽端口信息
REPLCONF listening-port <salve監(jiān)聽端口號(hào)>
master接收到到命令后,會(huì)記錄在主服務(wù)器對(duì)應(yīng)的客戶端狀態(tài) salve_listening_port屬性中
圖片
我們可以在master上執(zhí)行“info replication”命令可以看到從服務(wù)器的port
圖片
同步
slave會(huì)向master發(fā)送一個(gè) psync 命令來進(jìn)行數(shù)據(jù)同步,并且數(shù)據(jù)同步將分為全量同步和部分同步。
命令持續(xù)復(fù)制
在完成同步之后,master -- slave 會(huì)進(jìn)入到命令傳播階段、這個(gè)階段master將寫命令發(fā)送給slave,slave接收并執(zhí)行增量的命令同步實(shí)現(xiàn)主從一致。
畫個(gè)圖總結(jié)一下實(shí)現(xiàn)原理的流程吧:
圖片
?? 怎么判斷slave是第一次進(jìn)行主從復(fù)制呢?
Redis核心結(jié)構(gòu)server的cached_master保存了master節(jié)點(diǎn)的信息,只有進(jìn)行過主從復(fù)制才會(huì)賦值,否則為空。
復(fù)制方式
Redis復(fù)制的方式可分為全量復(fù)制和增量復(fù)制,不過在第一次全量復(fù)制之后,master和slave雙方之間就會(huì)維護(hù)一個(gè) TCP 長連接,后續(xù)master可以通過這個(gè)連接繼續(xù)將新寫操作命令同步給slave。
全量復(fù)制
全量復(fù)制意思是master當(dāng)前全部數(shù)據(jù)進(jìn)行復(fù)制同步到slave。
全量復(fù)制可分為三個(gè)階段,我們先看下圖有個(gè)整體印象,然后再看每個(gè)階段都干了啥!
圖片
?? 階段一 建立鏈接、協(xié)商同步
這部分流程我們?cè)凇緦?shí)現(xiàn)原理】章節(jié)講比較明白了,這個(gè)階段建立連接,并告訴master需要進(jìn)行同步,等確認(rèn)后就可以為后續(xù)的全量復(fù)制做準(zhǔn)備。
具體來說就是在 slave給master發(fā)送 psync 命令,表示要進(jìn)行數(shù)據(jù)同步,master會(huì)根據(jù)這個(gè)命令的參數(shù)來啟動(dòng)復(fù)制!
?? psync ? -1,命令參數(shù) ‘?’和 ‘-1’是什么意思?
這兩個(gè)參數(shù)分別表示 master 的唯一表示 runID 和復(fù)制進(jìn)度 offset ,因?yàn)槭堑谝淮螐?fù)制,此時(shí)是不知道m(xù)aster的runID的,所以設(shè)置‘ ?’,而 -1 表示第一次復(fù)制。
注:每個(gè) Redis 實(shí)例啟動(dòng)時(shí)都會(huì)自動(dòng)生成的一個(gè)隨機(jī) ID,這個(gè)就是runID
master在收到 psync 命令之后會(huì)用 FULLRESYNC 響應(yīng)命令帶上兩個(gè)參數(shù):master 的runID 和的復(fù)制進(jìn)度 offset,這個(gè)時(shí)候 salve 就可以記下這兩個(gè)參數(shù)值了。
FULLRESYNC : 意思是完全重新同步,就是會(huì)把當(dāng)前master的所有數(shù)據(jù)復(fù)制同步給salve
???? 階段是二 master同步數(shù)據(jù)給從slave
master 收到 psync 命令后,會(huì)執(zhí)行bgsave命令, fork 出一個(gè)子進(jìn)程,子進(jìn)程中將所有的數(shù)據(jù)按特定編碼存儲(chǔ)到 RDB(Redis Database) 文件中。
RDB生成完成之后就會(huì)把文件發(fā)送給salve,從庫接收到RDB文件后,先清空當(dāng)前數(shù)據(jù)庫數(shù)據(jù),然后才會(huì)加載RDB文件。
?? salve在加載RDB前清除數(shù)據(jù)的目的是什么?
salve 在執(zhí)行replicaof 或 slaveof 命令開始復(fù)制前,可能保存了其他數(shù)據(jù),清除是為了避免之前數(shù)據(jù)的影響
?? master在執(zhí)行bgsave期間新寫的命令并沒有生成到RDB中,數(shù)據(jù)丟失了嗎?
生成RDB的是fork出的子進(jìn)程做的,此時(shí)主進(jìn)程還是可以正常處理寫入命令的,此時(shí)為了保證主從的數(shù)據(jù)一致性master 會(huì)用專門的 replication buffer 來記錄 RDB 文件生成、加載RDB文件期間收到的所有寫操作。
?????? 階段三 master發(fā)送新寫操作命令給slave
master會(huì)把階段二期間記錄在 replication buffer 的寫命令,發(fā)送給salve,通過這種方式來實(shí)現(xiàn)主從的同步一致性。
?? 在什么情況下會(huì)進(jìn)行全量同步呢?
1. slave連接上master第一次復(fù)制的時(shí)候
2. 從節(jié)點(diǎn)發(fā)送 psync {runid} {offset} 時(shí),runid 與當(dāng)前主節(jié)點(diǎn)的 runid 不匹配則進(jìn)行全量復(fù)制
3. 從節(jié)點(diǎn)所需要同步數(shù)據(jù)的偏移量 offset 不在復(fù)制積壓緩沖區(qū)中
命令傳播
master在完成第一次同步后,就會(huì)基于長連接進(jìn)行后續(xù)命令傳播,master通過這個(gè)連接將寫命令傳播給slave,salve執(zhí)行得到的寫命令,從而保正主從數(shù)據(jù)的同步。
圖片
增量復(fù)制
我們知道在第一次全量復(fù)制后,主從之間使用長連接進(jìn)行命令傳播,但是如果網(wǎng)絡(luò)出現(xiàn)問題,出現(xiàn)閃斷(斷了一會(huì)又恢復(fù)了)那麻煩了,用戶可能從salve讀到舊數(shù)據(jù)。
這種情況下Redis 2.8開始會(huì)采用增量復(fù)制的方式繼續(xù)同步,如下圖流程:
圖片
問題的關(guān)鍵在于如何知道哪些數(shù)據(jù)作為增量發(fā)送給slave,在分析之前我們先了解幾個(gè)概念:
? 復(fù)制偏移量 (replication offset)
? 復(fù)制積壓緩沖區(qū) (replication backlog)
? 服務(wù)器運(yùn)行ID (runID)
?? 復(fù)制偏移量 ( replication offset)
主從節(jié)點(diǎn)都維護(hù)這一個(gè)復(fù)制偏移量,它代表著當(dāng)前節(jié)點(diǎn)接受數(shù)據(jù)的字節(jié)數(shù),注意這里表示的是【字節(jié)數(shù)】
圖片
上圖中master和slave的offset一開始是相等的,可以理解為數(shù)據(jù)一致,但是master此時(shí)增加了6個(gè)字節(jié)數(shù)據(jù),此時(shí)需要向salve傳播長度為6字節(jié)的數(shù)據(jù),從而保持主從數(shù)據(jù)的一致,offset一致。
? 想要知道主從是否一致,通過對(duì)比offset,也能知道
???? 復(fù)制積壓緩沖區(qū) ( replication backlog buffer )
復(fù)制積壓緩沖區(qū)是由master維護(hù)的一個(gè)固定長度的環(huán)形緩沖區(qū)、是先進(jìn)先出(FIFO)隊(duì)列,默認(rèn)大小為 1MB。
圖片
在命令傳播階段,除了發(fā)送命令給salve之外,還會(huì)寫入到復(fù)制積壓緩沖區(qū),此時(shí)salve還會(huì)做一個(gè)重要的事情,發(fā)送 REPLCONF ACK 命令給master,傳遞 replication_offset(slave當(dāng)前的復(fù)制偏移量),這個(gè)命令有三個(gè)作用:
? 檢測(cè)主從服務(wù)器的網(wǎng)絡(luò)狀態(tài)
? 輔助實(shí)現(xiàn) min-slaves 選項(xiàng)
? 檢測(cè)命令丟失
?????? 服務(wù)器運(yùn)行ID (runID)
不論主從都會(huì)有自己的運(yùn)行ID,在Redis服務(wù)器啟動(dòng)時(shí)會(huì)自動(dòng)生成,由40個(gè)隨機(jī)16進(jìn)制字符組成,第一次復(fù)制master會(huì)發(fā)送這個(gè)ID給到slave,而斷線重連時(shí)slave會(huì)帶上這個(gè)ID發(fā)送給master!
了解這些知識(shí)后,我們?cè)倮硪焕?,在網(wǎng)絡(luò)短暫斷開后,salve重新連上master時(shí),salve會(huì)通過 psync 命令將自己的復(fù)制偏移量 offset 發(fā)送給master,而master根據(jù)自己的offset 和 slave的offset 之間的差距,然后來決定對(duì)salve執(zhí)行哪種同步操作。
? 判斷出salve要讀取的數(shù)據(jù)還在 replication_backlog_buffer 里,那么主服務(wù)器將采用增量同步的方式;
? 判斷出讀取的數(shù)據(jù)已經(jīng)不存在 replication_backlog_buffer 里 (比如被覆蓋掉了),那么主服務(wù)器將采用全量同步的方式
?? replication_backlog_buffer 大小只有1M,數(shù)據(jù)被覆蓋的概率挺大,該如何配置避免呢?
replication_backlog_buffer 最小的大小可以這樣估算
repl_backlog_buffer = second * write_size_per_second
? second 為salve斷線后重新連接上master所需的平均 時(shí)間(以秒計(jì)算)。
? write_size_per_second 則是master平均每秒產(chǎn)生的寫命令數(shù)據(jù)量大小
這個(gè)配置我們看情況去定,這個(gè)參數(shù)在配置文件中如下,我們可以去修改它
repl-backlog-size 1mb
主從復(fù)制配置
配置方式
進(jìn)行配置主從復(fù)制還是比較簡單的,可以用 兩種方式:
1. 在從服務(wù)器中添加配置 slaveof 選項(xiàng),不過在5.0版本中使用了replicaof 代替了slaveof,雖然 slaveof可以繼續(xù)使用,建議使用 replicaof
2. 直接使用 slaveof 命令
# redis.conf文件進(jìn)行主從配置
# replicaof <masterip> <masterport>
replicaof 192.168.127.20 6379
配置好 redis.conf之后,我們分別啟動(dòng)主從服務(wù)器,可以用戶命令 info replication 查看復(fù)制信息
AUTH設(shè)置密碼
如果需要在 slave對(duì)master的建立連接是進(jìn)行驗(yàn)證,可以在master中配置requirepass選項(xiàng)設(shè)置密碼
那么需要在從服務(wù)器salve中使用該密碼,可以使用命令config set masterauth ,或者在配置文件中設(shè)置masterauth
總結(jié)
進(jìn)行主從復(fù)制之前需要master和slave進(jìn)行連接,只有連接成功之后才能進(jìn)行后續(xù)的復(fù)制動(dòng)作。
在主從服務(wù)器發(fā)送 pysnc 進(jìn)行第一次同步的時(shí)候,采用的是全量復(fù)制,而在同步完成之后,主從服務(wù)器都會(huì)維護(hù)著一個(gè)長連接,主服務(wù)器在接收到新的寫操作命令后,通過這個(gè)連接發(fā)送給從服務(wù)器,從而保持?jǐn)?shù)據(jù)的一致性。
如果遇到網(wǎng)絡(luò)閃斷情況,此時(shí)就進(jìn)行增量復(fù)制,不過需要確定復(fù)制積壓緩沖區(qū)是否覆蓋等情況才決定是進(jìn)行全量還是增量復(fù)制。