1.redis主從架構(gòu)的主要意義
(1).redis高并發(fā)對(duì)于高并發(fā)系統(tǒng)的意義
????????我們從高并發(fā)這個(gè)角度來(lái)探討一下這個(gè)問(wèn)題,要應(yīng)對(duì)高并發(fā)場(chǎng)景的話,僅僅就使用MySQL的話是不夠的,一般情況下MySQL能夠做到QPS2000左右,不過(guò)也不排除做了一系列復(fù)雜的分庫(kù)分表的MySQL,比如強(qiáng)事務(wù)要求的訂單系統(tǒng),QPS做到幾萬(wàn)也算很高了,但是這樣代價(jià)是很高的。但是一個(gè)單機(jī)的redis如果只是做一些簡(jiǎn)單的k,v操作的話很容易就可以做到幾萬(wàn)。所以說(shuō),要做一個(gè)高并發(fā)系統(tǒng)的話,把底層緩存做得很好是不可避免的,但是僅僅使用Redis是不夠的,但是redis是整個(gè)大型的能夠支撐高并發(fā)的緩存架構(gòu)中非常重要的一個(gè)環(huán)節(jié)。要做一個(gè)能夠支撐起數(shù)十萬(wàn)甚至上百萬(wàn)并發(fā)的系統(tǒng),底層的緩存中間件、緩存系統(tǒng)要能支撐這種程度的高并發(fā),其次還需要良好的緩存架構(gòu)設(shè)計(jì),比如多級(jí)緩存架構(gòu)、提高熱點(diǎn)緩存的命中。
(2).Redis用來(lái)做高并發(fā)的瓶頸在哪?
????????其實(shí)這個(gè)問(wèn)題的的答案就兩個(gè)字:?jiǎn)螜C(jī)。
(3).怎樣才能使redis能夠支撐起超過(guò)10萬(wàn)+的并發(fā)?
????????首先說(shuō)一下特殊情況,如果你的服務(wù)器配置超級(jí)高,維護(hù)也做得很到位,對(duì)于Redis的操作也很簡(jiǎn)單,那么QPS過(guò)十萬(wàn)是很有可能的,但是在一般情況下單機(jī)的Redis只能有幾萬(wàn)的QPS。
如果用Redis做讀寫(xiě)分離架構(gòu),面對(duì)那種讀請(qǐng)求很多,寫(xiě)請(qǐng)求不是很多的場(chǎng)景是非常好的。(如果讀請(qǐng)求很多的話,可以使用異步隊(duì)列實(shí)現(xiàn),這里因?yàn)槟芰τ邢蘧筒唤榻B了。)采用讀寫(xiě)分離的Redis主從架構(gòu)破10萬(wàn)的QPS還是不是很難。

上圖為Redis的主從架構(gòu)的簡(jiǎn)單說(shuō)明。
(4).redis主從復(fù)制的機(jī)制
(a).redis采用異步方式復(fù)制數(shù)據(jù)到slave節(jié)點(diǎn),slave node會(huì)周期性地確認(rèn)自己每次復(fù)制的數(shù)據(jù)量。
(b).一個(gè)master node是可以配置多個(gè)slave node的。
(c).slave node也可以連接其他的slave node。
(d).slave node做復(fù)制的時(shí)候,是不會(huì)影響master node的正常工作的。
(e).slave node在做復(fù)制的時(shí)候,也不會(huì)影響自己的查詢操作,它會(huì)用舊的數(shù)據(jù)集來(lái)提供服務(wù); 但是復(fù)制完成的時(shí)候,需要?jiǎng)h除舊數(shù)據(jù)集,加載新數(shù)據(jù)集,這個(gè)時(shí)候就會(huì)暫停對(duì)外服務(wù)了。
(f).slave node主要用來(lái)進(jìn)行水平擴(kuò)容,做讀寫(xiě)分離,擴(kuò)容的slave node可以提高讀的吞吐量。
(5).master持久化對(duì)于主從架構(gòu)的安全保障的意義
????????如果是使用了redis的主從架構(gòu)的話,建議要打開(kāi)master節(jié)點(diǎn)的持久化,不建議使用slave節(jié)點(diǎn)進(jìn)行熱備,如果你的master節(jié)點(diǎn)沒(méi)有持久化的話,master節(jié)點(diǎn)如果宕機(jī)重啟的話,master節(jié)點(diǎn)里面的數(shù)據(jù)都是空的,可能一經(jīng)過(guò)復(fù)制,slave節(jié)點(diǎn)里的數(shù)據(jù)全部被清空。所以說(shuō),master節(jié)點(diǎn)要把持久化開(kāi)起來(lái)。而且master節(jié)點(diǎn)的冷備也要做起來(lái),萬(wàn)一數(shù)據(jù)沒(méi)有恢復(fù)成功,拷貝一份RDB過(guò)來(lái),只有master節(jié)點(diǎn)的數(shù)據(jù)恢復(fù)了,slave節(jié)點(diǎn)的數(shù)據(jù)也就恢復(fù)了。
????????還有就是redis的哨兵機(jī)制,如果結(jié)合哨兵機(jī)制來(lái)做高可用性的話,也需要對(duì)master做持久化,因?yàn)楹芸赡苌诒鴻C(jī)制還沒(méi)有檢測(cè)到master節(jié)點(diǎn)宕機(jī),然后master節(jié)點(diǎn)重啟后就把數(shù)據(jù)清空了。
2.主從架構(gòu)的一些原理簡(jiǎn)介
(1).全量復(fù)制的原理簡(jiǎn)介
????????首先slave節(jié)點(diǎn)會(huì)先ping一下master節(jié)點(diǎn),看能不能ping通,如果slave節(jié)點(diǎn)是第一次連接master節(jié)點(diǎn),那么master節(jié)點(diǎn)會(huì)執(zhí)行一個(gè)操作:全量從復(fù)制(full resynchronization),即master將他所有的數(shù)據(jù)一次性給這個(gè)slave節(jié)點(diǎn)。如果這個(gè)節(jié)點(diǎn)是重新連上master節(jié)點(diǎn),那么master節(jié)點(diǎn)不會(huì)把全部的數(shù)據(jù)給他,而是給他部分新的數(shù)據(jù)。
????????開(kāi)始full resynchronization的時(shí)候,master會(huì)啟動(dòng)一個(gè)后臺(tái)線程,開(kāi)始生成一份RDB快照文件,同時(shí)還會(huì)將從客戶端收到的所有寫(xiě)命令緩存在內(nèi)存中。RDB文件生成完畢之后,master會(huì)將這個(gè)RDB發(fā)送給slave,slave會(huì)先寫(xiě)入本地磁盤(pán),然后再?gòu)谋镜卮疟P(pán)加載到內(nèi)存中。然后master會(huì)將內(nèi)存中緩存的寫(xiě)命令發(fā)送給slave,slave也會(huì)同步這些數(shù)據(jù)。
????????slave node如果跟master node有網(wǎng)絡(luò)故障,斷開(kāi)了連接,會(huì)自動(dòng)重連。master如果發(fā)現(xiàn)有多個(gè)slave node都來(lái)重新連接,僅僅會(huì)啟動(dòng)一個(gè)rdb save操作,用一份數(shù)據(jù)服務(wù)所有slave node。


(2).主從復(fù)制的斷點(diǎn)續(xù)傳
????????從redis 2.8開(kāi)始,就支持主從復(fù)制的斷點(diǎn)續(xù)傳,如果主從復(fù)制過(guò)程中,網(wǎng)絡(luò)連接斷掉了,那么可以接著上次復(fù)制的地方,繼續(xù)復(fù)制下去,而不是從頭開(kāi)始復(fù)制一份,master node會(huì)在內(nèi)存中創(chuàng)建一個(gè)backlog,master和slave都會(huì)保存一個(gè)replica offset還有一個(gè)master id,offset就是保存在backlog中的。如果master和slave網(wǎng)絡(luò)連接斷掉了,slave會(huì)讓master從上次的replica offset開(kāi)始繼續(xù)復(fù)制,但是如果沒(méi)有找到對(duì)應(yīng)的offset,那么就會(huì)執(zhí)行一次resynchronization。
(3).無(wú)磁盤(pán)化復(fù)制
master在內(nèi)存中直接創(chuàng)建rdb,然后發(fā)送給slave,不會(huì)在自己本地落地磁盤(pán)了。

????????repl-diskless-sync 開(kāi)關(guān)。
????????repl-diskless-sync-delay 生成完RDB文件之后可以設(shè)置等待一段時(shí)間,這樣可以給更多的slave節(jié)點(diǎn)復(fù)制。傳輸一旦開(kāi)始就不會(huì)接受新的slave節(jié)點(diǎn)的復(fù)制請(qǐng)求,
(4).過(guò)期key處理
????????slave不會(huì)過(guò)期key,只會(huì)等待master過(guò)期key。如果master過(guò)期了一個(gè)key,或者通過(guò)LRU淘汰了一個(gè)key,那么會(huì)模擬一條del命令發(fā)送給slave。
3.redis主從架構(gòu)的一些核心機(jī)制筆記
(1).主從復(fù)制的完整流程介紹
a.slave node啟動(dòng),保存master node的信息,包括master node的host和ip,但此時(shí)復(fù)制流程沒(méi)開(kāi)始,master host和ip保存在redis.conf里面,配置項(xiàng)為:slaveof <masterip> <masterport>。
b.slave node內(nèi)部有個(gè)定時(shí)任務(wù),每秒檢查是否有新的master node要連接和復(fù)制,如果發(fā)現(xiàn),就跟master node建立socket網(wǎng)絡(luò)連接。
c.slave node發(fā)送ping命令給master node。
d.口令認(rèn)證,如果master設(shè)置了requirepass,那么salve node必須發(fā)送masterauth的口令過(guò)去進(jìn)行認(rèn)證。
e.master node第一次執(zhí)行全量復(fù)制,將所有數(shù)據(jù)發(fā)給slave node。
f.master node后續(xù)持續(xù)將寫(xiě)命令,異步復(fù)制給slave node。

(2).數(shù)據(jù)同步相關(guān)的核心機(jī)制
a.master和slave都會(huì)維護(hù)一個(gè)offset
????????master會(huì)在自身不斷累加offset,slave也會(huì)在自身不斷累加offset,slave每秒都會(huì)上報(bào)自己的offset給master,同時(shí)master也會(huì)保存每個(gè)slave的offset,由offset來(lái)判斷兩者的數(shù)據(jù)是否一致。
b.backlog
????????master node有一個(gè)backlog,默認(rèn)是1MB大小,master node給slave node復(fù)制數(shù)據(jù)時(shí),也會(huì)將數(shù)據(jù)在backlog中同步寫(xiě)一份,backlog主要是用來(lái)做全量復(fù)制中斷后的增量復(fù)制。
c.master run id
????????通過(guò)host+ip我們可以定位master node,但是我們不能通過(guò)他們來(lái)判斷master的數(shù)據(jù)是否同步,如果master node重啟或者數(shù)據(jù)出現(xiàn)了變化,那么slave node應(yīng)該根據(jù)不同的run id區(qū)分,比如說(shuō),你使用一份RDB冷備來(lái)恢復(fù)master的數(shù)據(jù),master不僅僅是數(shù)據(jù)產(chǎn)生了變化,runid也會(huì)發(fā)生變化,run id不同就馬上進(jìn)行全量復(fù)制。如果需要不更改run id重啟redis,可以使用redis-cli debug reload命令。
d.psync
????????slave節(jié)點(diǎn)發(fā)送psync相關(guān)的指令給master node進(jìn)行復(fù)制,使用psync將 runid和 offset發(fā)送給master,master node會(huì)根據(jù)自身的情況返回響應(yīng)信息,如果是runid發(fā)生了變化,他就會(huì)返回FULLRESYNC runid offset觸發(fā)全量復(fù)制,如果runid沒(méi)有發(fā)生變化則返回CONTINUE觸發(fā)增量復(fù)制。
(3).全量復(fù)制的流程
a.master執(zhí)行bgsave,在本地生成一份rdb快照文件。
b.master node將rdb快照文件發(fā)送給salve node,如果rdb復(fù)制時(shí)間超過(guò)60秒(可以通過(guò)配置repl-timeout來(lái)設(shè)定),那么slave node就會(huì)認(rèn)為復(fù)制失敗,可以根據(jù)實(shí)際情況適當(dāng)調(diào)節(jié)大這個(gè)參數(shù)。(對(duì)于千兆網(wǎng)卡的機(jī)器,一般每秒傳輸100MB,6G文件,很可能超過(guò)60s)。
c.master node在生成rdb時(shí),會(huì)將所有新的寫(xiě)命令緩存在內(nèi)存中,在salve node保存了rdb之后,再將新的寫(xiě)命令復(fù)制給salve node。
d.client-output-buffer-limit slave 256MB 64MB 60,如果在復(fù)制期間,內(nèi)存緩沖區(qū)持續(xù)消耗超過(guò)64MB,或者一次性超過(guò)256MB,那么停止復(fù)制,復(fù)制失敗。
e.slave node接收到rdb之后,先將RDB文件持久化到硬盤(pán)中,然后清空自己的舊數(shù)據(jù),然后重新加載rdb文件到本機(jī)的內(nèi)存中,同時(shí)基于舊的數(shù)據(jù)版本對(duì)外提供服務(wù)。
f.如果slave node開(kāi)啟了AOF,那么會(huì)立即執(zhí)行BGREWRITEAOF,重寫(xiě)AOF。
ps:全量復(fù)制還是很耗費(fèi)時(shí)間的,因?yàn)槠溟g有rdb文件的生成,通過(guò)網(wǎng)絡(luò)傳輸rdb文件,slave舊數(shù)據(jù)的清理,salve aof開(kāi)啟的話還需要進(jìn)行rewrite,所以很耗費(fèi)時(shí)間,如果復(fù)制的數(shù)據(jù)量在4G~6G之間,那么很可能全量復(fù)制時(shí)間消耗到1分半到2分鐘。
(4).增量復(fù)制
a.如果全量復(fù)制過(guò)程中,master-slave網(wǎng)絡(luò)連接斷掉,那么salve重新連接master時(shí),會(huì)觸發(fā)增量復(fù)制。
b.master直接從自己的backlog中獲取部分丟失的數(shù)據(jù),發(fā)送給slave node,默認(rèn)backlog就是1MB。
c.msater就是根據(jù)slave發(fā)送的psync中的offset來(lái)從backlog中獲取數(shù)據(jù)的。
(5).heartbeat
當(dāng)全量復(fù)制完畢之后,主從節(jié)點(diǎn)互相都會(huì)發(fā)送heartbeat信息,master默認(rèn)每隔10秒發(fā)送一次heartbeat,salve node每隔1秒發(fā)送一個(gè)heartbeat。
(6).異步復(fù)制
主從節(jié)點(diǎn)正常工作時(shí),master每次接收到寫(xiě)命令之后,先在內(nèi)部寫(xiě)入數(shù)據(jù),然后異步發(fā)送給slave node。