Redis的高可靠性有兩層含義:一是數(shù)據(jù)盡量少丟失,二是服務(wù)盡量少中斷。數(shù)Redis的AOF 和 RDB這兩種持久化機(jī)制保證了前者,而對(duì)于后者,Redis 的做法就是增加副本冗余量,將一份數(shù)據(jù)同時(shí)保存在多個(gè)實(shí)例上。
而這個(gè)所謂的一份數(shù)據(jù)多個(gè)示例就是我們說的Redis 的主從庫(kù)模式(當(dāng)然還有由他的衍生出的哨兵和集群模式),以保證數(shù)據(jù)副本的一致,主從庫(kù)之間采用的是讀寫分離的方式。如下所示:
讀操作:主庫(kù)、從庫(kù)都可以接收;
寫操作:首先到主庫(kù)執(zhí)行,然后,主庫(kù)將寫操作同步給從庫(kù)。
主從模式一旦采用了讀寫分離,所有數(shù)據(jù)的修改只會(huì)在主庫(kù)上進(jìn)行,不用協(xié)調(diào)三個(gè)實(shí)例。主庫(kù)有了最新的數(shù)據(jù)后,會(huì)同步給從庫(kù),這樣,主從庫(kù)的數(shù)據(jù)就是一致的。
主從之間如何同步
當(dāng)我們啟動(dòng)多個(gè) Redis 實(shí)例的時(shí)候,它們相互之間就可以通過 replicaof(Redis 5.0 之前使用 slaveof)命令形成主庫(kù)和從庫(kù)的關(guān)系,之后會(huì)按照三個(gè)階段完成數(shù)據(jù)的第一次同步。
例如,現(xiàn)在有實(shí)例 1(ip:172.16.19.3)和實(shí)例 2(ip:172.16.19.5),我們?cè)趯?shí)例 2 上執(zhí)行以下這個(gè)命令后,實(shí)例 2 就變成了實(shí)例 1 的從庫(kù),并從實(shí)例 1 上復(fù)制數(shù)據(jù):replicaof 172.16.19.3 6379。三階段的同步如下所示:
上圖中FULLRESYNC 響應(yīng)表示第一次復(fù)制采用的全量復(fù)制,也就是說,主庫(kù)會(huì)把當(dāng)前所有的數(shù)據(jù)都復(fù)制給從庫(kù)。
上面說的主從庫(kù)模式中,所有的從庫(kù)都是和主庫(kù)連接,所有的全量復(fù)制也都是和主庫(kù)進(jìn)行的。現(xiàn)在,我們可以通過“主 - 從 - 從”模式將主庫(kù)生成 RDB 和傳輸 RDB 的壓力,以級(jí)聯(lián)的方式分散到從庫(kù)上。
可以在從庫(kù)執(zhí)行如下命令建立主從關(guān)系:
replicaof 所選從庫(kù)的IP 6379
主從庫(kù)間網(wǎng)絡(luò)中斷問題
上面我們了解了主從庫(kù)間通過全量復(fù)制實(shí)現(xiàn)數(shù)據(jù)同步的過程,以及通過“主 - 從 - 從”模式分擔(dān)主庫(kù)壓力的方式,這個(gè)過程也稱為基于長(zhǎng)連接的命令傳播,可以避免頻繁建立連接的開銷。但是這個(gè)連接還存在中斷的風(fēng)險(xiǎn)。
從 Redis 2.8 開始,網(wǎng)絡(luò)斷了之后,主從庫(kù)會(huì)采用增量復(fù)制的方式繼續(xù)同步。
當(dāng)主從庫(kù)斷連后,主庫(kù)會(huì)把斷連期間收到的寫操作命令,寫入 replication buffer,同時(shí)也會(huì)把這些操作命令也寫入 repl_backlog_buffer 這個(gè)緩沖區(qū)。repl_backlog_buffer 是一個(gè)環(huán)形緩沖區(qū),主庫(kù)會(huì)記錄自己寫到的位置,從庫(kù)則會(huì)記錄自己已經(jīng)讀到的位置。增量復(fù)制的過程如下圖所示:
不過,有一個(gè)地方我要強(qiáng)調(diào)一下,因?yàn)?repl_backlog_buffer 是一個(gè)環(huán)形緩沖區(qū),所以在緩沖區(qū)寫滿后,主庫(kù)會(huì)繼續(xù)寫入,此時(shí),就會(huì)覆蓋掉之前寫入的操作。如果從庫(kù)的讀取速度比較慢,就有可能導(dǎo)致從庫(kù)還未讀取的操作被主庫(kù)新寫的操作覆蓋了,這會(huì)導(dǎo)致主從庫(kù)間的數(shù)據(jù)不一致。
因此,我們要想辦法避免這個(gè)情況,一般而言,我們可以調(diào)整 repl_backlog_size 這個(gè)參數(shù)。這個(gè)參數(shù)和所需的緩沖空間大小有關(guān)。緩沖空間的計(jì)算公式是:緩沖空間大小 = 主庫(kù)寫入命令速度 * 操作大小 - 主從庫(kù)間網(wǎng)絡(luò)傳輸命令速度 * 操作大小。在實(shí)際應(yīng)用中,考慮到可能存在一些突發(fā)的請(qǐng)求壓力,我們通常需要把這個(gè)緩沖空間擴(kuò)大一倍,即 repl_backlog_size = 緩沖空間大小 * 2,這也就是 repl_backlog_size 的最終值。
總結(jié)來說,Redis的主從同步有三種模式:全量復(fù)制、基于長(zhǎng)連接的命令傳播,以及增量復(fù)制。主從庫(kù)的環(huán)境啟動(dòng)后需要進(jìn)行全量復(fù)制,長(zhǎng)連接復(fù)制是主從庫(kù)正常運(yùn)行后的常規(guī)同步階段。在這個(gè)階段中,主從庫(kù)之間通過命令傳播實(shí)現(xiàn)同步。不過,這期間如果遇到了網(wǎng)絡(luò)斷連,增量復(fù)制就派上用場(chǎng)了。
通常來說一個(gè) Redis 實(shí)例的數(shù)據(jù)庫(kù)不要太大,一個(gè)實(shí)例大小在幾 GB 級(jí)別比較合適,這樣可以減少 RDB 文件生成、傳輸和重新加載的開銷。