Redis主從復(fù)制講解!
使用和配置主從復(fù)制,能使得從 Redis 服務(wù)器( slave)能精確得復(fù)制主 Redis 服務(wù)器( master)的內(nèi)容。每次當(dāng) slave 和 master 之間的連接斷開(kāi)時(shí), slave 會(huì)自動(dòng)重連到 master 上,并且無(wú)論這期間 master 發(fā)生了什么, slave 都將嘗試讓自身成為 master 的精確副本。
主從復(fù)制的配置要點(diǎn):
- 配從庫(kù)不配主,從庫(kù)配置:slaveof 主庫(kù)IP 主庫(kù)端口
- 查看redis的配置信息:info replication
這個(gè)系統(tǒng)的運(yùn)行依靠三個(gè)主要的機(jī)制:
- 當(dāng)一個(gè) master 實(shí)例和一個(gè) slave 實(shí)例連接正常時(shí), master 會(huì)發(fā)送一連串的命令流來(lái)保持對(duì) slave 的更新,以便于將自身數(shù)據(jù)集的改變復(fù)制給 slave :包括客戶端的寫入、key 的過(guò)期或被逐出等等。
- 當(dāng) master 和 slave 之間的連接斷開(kāi)之后,因?yàn)榫W(wǎng)絡(luò)問(wèn)題、或者是主從意識(shí)到連接超時(shí), slave 重新連接上 master 并會(huì)嘗試進(jìn)行部分重同步:這意味著它會(huì)嘗試只獲取在斷開(kāi)連接期間內(nèi)丟失的命令流。
- 當(dāng)無(wú)法進(jìn)行部分重同步時(shí), slave 會(huì)請(qǐng)求進(jìn)行全量重同步。這會(huì)涉及到一個(gè)更復(fù)雜的過(guò)程,例如 master 需要?jiǎng)?chuàng)建所有數(shù)據(jù)的快照,將之發(fā)送給 slave ,之后在數(shù)據(jù)集更改時(shí)持續(xù)發(fā)送命令流到 slave 。
主從復(fù)制的簡(jiǎn)單性質(zhì):
- 一個(gè)master可以有多個(gè)slave
- 每個(gè)slave只能有一個(gè)master
- 每個(gè)slave也可以有自己的多個(gè)slave
- 數(shù)據(jù)流是單向的,從master到slave
主從復(fù)制的缺點(diǎn):
由于所有的寫操作都是先在Master上操作,然后同步更新到Slave上,所以從Master同步到Slave服務(wù)器有一定的延遲,當(dāng)系統(tǒng)很繁忙的時(shí)候,延遲問(wèn)題會(huì)更加嚴(yán)重,Slave機(jī)器數(shù)量的增加也會(huì)使這個(gè)問(wèn)題更加嚴(yán)重。
如果master宕機(jī)了,默認(rèn)情況下不會(huì)在slave節(jié)點(diǎn)中自動(dòng)選擇一個(gè)master(不能進(jìn)行寫操作),所以有哨兵和集群的概念。
Redis為什么需要主從復(fù)制
使用Redis主從復(fù)制的原因主要是單臺(tái)Redis節(jié)點(diǎn)存在以下的局限性:
- Redis雖然讀寫的速度都很快,單節(jié)點(diǎn)的Redis能夠支撐QPS大概在5w左右,如果上千萬(wàn)的用戶訪問(wèn),Redis就承載不了,成為了高并發(fā)的瓶頸。
- 單節(jié)點(diǎn)的Redis不能保證高可用,當(dāng)Redis因?yàn)槟承┰蛞馔忮礄C(jī)時(shí),會(huì)導(dǎo)致緩存不可用。
- CPU的利用率上,單臺(tái)Redis實(shí)例只能利用單個(gè)核心,這單個(gè)核心在面臨海量數(shù)據(jù)的存取和管理工作時(shí)壓力會(huì)非常大。
Redis主從復(fù)制的策略
從總體上來(lái)說(shuō),Redis主從復(fù)制的策略就是:當(dāng)主從服務(wù)器剛建立連接的時(shí)候,進(jìn)行全量同步;全量復(fù)制結(jié)束后,進(jìn)行增量復(fù)制。當(dāng)然,如果有需要,slave 在任何時(shí)候都可以發(fā)起全量同步。
主從全量復(fù)制的流程:
Redis全量復(fù)制一般發(fā)生在Slave初始化階段,這時(shí)Slave需要將Master上的所有數(shù)據(jù)都復(fù)制一份,具體步驟如下:
- slave服務(wù)器連接到master服務(wù)器,便開(kāi)始進(jìn)行數(shù)據(jù)同步,發(fā)送psync命令(Redis2.8之前是sync命令)
- master服務(wù)器收到psync命令之后,開(kāi)始執(zhí)行bgsave命令生成RDB快照文件并使用緩存區(qū)記錄此后執(zhí)行的所有寫命令
- -如果master收到了多個(gè)slave并發(fā)連接請(qǐng)求,它只會(huì)進(jìn)行一次持久化,而不是每個(gè)連接都執(zhí)行一次,然后再把這一份持久化的數(shù)據(jù)發(fā)送給多個(gè)并發(fā)連接的slave。如果RDB復(fù)制時(shí)間超過(guò)60秒(repl-timeout),那么slave服務(wù)器就會(huì)認(rèn)為復(fù)制失敗,可以適當(dāng)調(diào)節(jié)大這個(gè)參數(shù)
- master服務(wù)器bgsave執(zhí)行完之后,就會(huì)向所有Slava服務(wù)器發(fā)送快照文件,并在發(fā)送期間繼續(xù)在緩沖區(qū)內(nèi)記錄被執(zhí)行的寫命令
- client-output-buffer-limit slave 256MB 64MB 60,如果在復(fù)制期間,內(nèi)存緩沖區(qū)持續(xù)消耗超過(guò)64MB,或者一次性超過(guò)256MB,那么停止復(fù)制,復(fù)制失敗
- slave服務(wù)器收到RDB快照文件后,會(huì)將接收到的數(shù)據(jù)寫入磁盤,然后清空所有舊數(shù)據(jù),在從本地磁盤載入收到的快照到內(nèi)存中,同時(shí)基于舊的數(shù)據(jù)版本對(duì)外提供服務(wù)。
- slave服務(wù)器完成對(duì)快照的載入,開(kāi)始接收命令請(qǐng)求,并執(zhí)行來(lái)自主服務(wù)器緩沖區(qū)的寫命令;
- 如果slave 節(jié)點(diǎn)開(kāi)啟了AOF,那么會(huì)立即執(zhí)行BGREWRITEAOF,重寫AOF
增量復(fù)制:
Redis的增量復(fù)制是指在初始化的全量復(fù)制并開(kāi)始正常工作之后,master服務(wù)器將發(fā)生的寫操作同步到slave服務(wù)器的過(guò)程,
增量復(fù)制的過(guò)程主要是master服務(wù)器每執(zhí)行一個(gè)寫命令就會(huì)向slave服務(wù)器發(fā)送相同的寫命令,slave服務(wù)器接收并執(zhí)行收到的寫命令。
斷點(diǎn)續(xù)傳:
什么是斷點(diǎn)續(xù)傳:
slave與master能夠在網(wǎng)絡(luò)連接斷開(kāi)重連后,只從中斷處繼續(xù)進(jìn)行復(fù)制,而不必重新同步,這就是所謂的斷點(diǎn)續(xù)傳。
斷電續(xù)傳這個(gè)新特性使用psync命令,master服務(wù)器收到slave發(fā)送的psync命令后,會(huì)根據(jù)自身的情況做出對(duì)應(yīng)的處理,可能是FULLRESYNC runid offset觸發(fā)全量復(fù)制,也可能是CONTINUE觸發(fā)增量復(fù)制
命令格式:psync runid offset
3.2、工作原理:
(1)master服務(wù)器在內(nèi)存緩沖區(qū)中給每個(gè)slave服務(wù)器都維護(hù)了一份同步備份日志(in-memory backlog),緩存最近一段時(shí)間的數(shù)據(jù),默認(rèn)大小1m,如果超過(guò)這個(gè)大小就會(huì)清理掉。
(2)同時(shí),master 和 slave 服務(wù)器都維護(hù)了一個(gè)復(fù)制偏移量(replication offset)和 master線程ID(master run id),每個(gè)slave服務(wù)器在跟master服務(wù)器進(jìn)行同步時(shí)都會(huì)攜帶master run id 和 最后一次同步的復(fù)制偏移量offset,通過(guò)offset可以知道主從之間的數(shù)據(jù)不一致的情況。
(3)當(dāng)連接斷開(kāi)時(shí),slave服務(wù)器會(huì)重新連接上master服務(wù)器,然后請(qǐng)求繼續(xù)復(fù)制。假如主從服務(wù)器的兩個(gè)master run id相同,并且指定的偏移量offset在同步備份日志中還有效,復(fù)制就會(huì)從上次中斷的點(diǎn)開(kāi)始繼續(xù)。如果其中一個(gè)條件不滿足,就會(huì)進(jìn)行完全重新同步,因?yàn)橹鬟\(yùn)行id不保存在磁盤中,如果從服務(wù)器重啟的話就只能進(jìn)行完全同步了。
master服務(wù)器維護(hù)的offset是存儲(chǔ)在backlog中,msater就是根據(jù)slave發(fā)送的offset來(lái)從backlog中獲取數(shù)據(jù)的。
(4)在部分同步過(guò)程中,master會(huì)將本地記錄的同步備份日志中記錄的指令依次發(fā)送給slave服務(wù)器從而達(dá)到數(shù)據(jù)一致。
什么是run_id
run_id是Redis 服務(wù)器的隨機(jī)標(biāo)識(shí)符,用于 Sentinel 和集群,服務(wù)重啟后就會(huì)改變;
當(dāng)slave節(jié)點(diǎn)復(fù)制時(shí)發(fā)現(xiàn)和之前的 run_id 不同時(shí),將會(huì)對(duì)數(shù)據(jù)進(jìn)行全量同步。
查看runid
redis-cli -p 6379 info server | grep run
run_id:345dda992e5064bc80e01f96ea90f729b722b2ea
什么是偏移量:
通過(guò)對(duì)比主從節(jié)點(diǎn)的復(fù)制偏移量,可以判斷主從節(jié)點(diǎn)數(shù)據(jù)是否一致。
- 參與復(fù)制的主從節(jié)點(diǎn)都會(huì)維護(hù)自身的復(fù)制偏移量。主節(jié)點(diǎn)(master)在處理完寫命令后,會(huì)把命令的字節(jié)長(zhǎng)度做累加記錄,統(tǒng)計(jì)信息在info replication中的master_repl_offset指標(biāo)中。
- 從節(jié)點(diǎn)每秒上報(bào)自身的復(fù)制偏移量給主節(jié)點(diǎn),因此主節(jié)點(diǎn)也會(huì)保存從節(jié)點(diǎn)的復(fù)制偏移量
- 從節(jié)點(diǎn)在接收到主節(jié)點(diǎn)發(fā)送的命令后,也會(huì)累加記錄自身的偏移量。統(tǒng)計(jì)在info replication中的slave_repl_offset指標(biāo)中
無(wú)磁盤化復(fù)制:
在前面全量復(fù)制的過(guò)程中,master會(huì)將數(shù)據(jù)保存在磁盤的rdb文件中然后發(fā)送給slave服務(wù)器,但如果master上的磁盤空間有限或者是使用比較低速的磁盤,這種操作會(huì)給master服務(wù)器帶來(lái)較大的壓力,那怎么辦呢?在Redis2.8之后,可以通過(guò)無(wú)盤復(fù)制來(lái)達(dá)到目的,由master直接開(kāi)啟一個(gè)socket,在內(nèi)存中創(chuàng)建RDB文件,再將rdb文件發(fā)送給slave服務(wù)器,不使用磁盤作為中間存儲(chǔ)。(無(wú)盤復(fù)制一般應(yīng)用在磁盤空間有限但是網(wǎng)絡(luò)狀態(tài)良好的情況下)
repl-diskless-sync :是否開(kāi)啟無(wú)磁盤復(fù)制
repl-diskless-sync-delay:等待一定時(shí)長(zhǎng)再開(kāi)始復(fù)制,因?yàn)橐雀鄐lave重新連接過(guò)來(lái)
主從復(fù)制實(shí)現(xiàn):
主從復(fù)制命令:
#在希望成為slave的節(jié)點(diǎn)中執(zhí)行命令(改換門庭)
slaveof ${masterIP} ${masterPort}
#此過(guò)程會(huì)異步第將master節(jié)點(diǎn)中的數(shù)據(jù)全量復(fù)制到當(dāng)前節(jié)點(diǎn)中(如果使用命令模式,只有當(dāng)次生效,例:重啟)
#主從復(fù)制(配從庫(kù),不配主庫(kù)
repliceof ${masterIP} ${masterPort}
#在不希望作為slave的節(jié)點(diǎn)中執(zhí)行以下命令
salveof no one
#查看當(dāng)前節(jié)點(diǎn)是否主從
info replication
#從機(jī)訪問(wèn)主機(jī)的通行密碼,如果master設(shè)置了登錄密碼
masterauth ${password}
相關(guān)配置 :
master宕機(jī)故障
redis將無(wú)法執(zhí)行寫請(qǐng)求,只有slave節(jié)點(diǎn)能執(zhí)行讀請(qǐng)求,影響了系統(tǒng)的可用性
方法1:
隨機(jī)找一個(gè)節(jié)點(diǎn),執(zhí)行slaveof no one,使其成為master節(jié)點(diǎn)
然后對(duì)其他slave節(jié)點(diǎn)執(zhí)行slaveof newMatserIp newMasterPort
方法2:
馬上重啟master節(jié)點(diǎn),它將會(huì)重新成為master
方法三:哨兵模式
Redis 復(fù)制如何處理 key 的過(guò)期
Redis 的過(guò)期機(jī)制可以限制 key 的生存時(shí)間。此功能取決于 Redis 實(shí)例計(jì)算時(shí)間的能力,但是,即使使用 Lua 腳本更改了這些 key,Redis slaves 也能正確地復(fù)制具有過(guò)期時(shí)間的 key。
為了實(shí)現(xiàn)這樣的功能,Redis 不能依靠主從使用同步時(shí)鐘,因?yàn)檫@是一個(gè)無(wú)法解決的并且會(huì)導(dǎo)致 race condition 和數(shù)據(jù)集不一致的問(wèn)題,所以 Redis 使用三種主要的技術(shù)使過(guò)期的 key 的復(fù)制能夠正確工作:
- slave 不會(huì)讓 key 過(guò)期,而是等待 master 讓 key 過(guò)期。當(dāng)一個(gè) master 讓一個(gè) key 到期(或由于 LRU 算法將之驅(qū)逐)時(shí),它會(huì)合成一個(gè) DEL 命令并傳輸?shù)剿械?slave。
- 但是,由于這是 master 驅(qū)動(dòng)的 key 過(guò)期行為,master 無(wú)法及時(shí)提供 DEL 命令,所以有時(shí)候 slave 的內(nèi)存中仍然可能存在在邏輯上已經(jīng)過(guò)期的 key 。為了處理這個(gè)問(wèn)題,slave 使用它的邏輯時(shí)鐘以報(bào)告只有在不違反數(shù)據(jù)集的一致性的讀取操作(從主機(jī)的新命令到達(dá))中才存在 key。用這種方法,slave 避免報(bào)告邏輯過(guò)期的 key 仍然存在。在實(shí)際應(yīng)用中,使用 slave 程序進(jìn)行縮放的 HTML 碎片緩存,將避免返回已經(jīng)比期望的時(shí)間更早的數(shù)據(jù)項(xiàng)。
- 在Lua腳本執(zhí)行期間,不執(zhí)行任何 key 過(guò)期操作。當(dāng)一個(gè)Lua腳本運(yùn)行時(shí),從概念上講,master 中的時(shí)間是被凍結(jié)的,這樣腳本運(yùn)行的時(shí)候,一個(gè)給定的鍵要么存在要么不存在。這可以防止 key 在腳本中間過(guò)期,保證將相同的腳本發(fā)送到 slave ,從而在二者的數(shù)據(jù)集中產(chǎn)生相同的效果。
一旦一個(gè) slave 被提升為一個(gè) master ,它將開(kāi)始獨(dú)立地過(guò)期 key,而不需要任何舊 master 的幫助。