溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

分布式Redis深度歷險-復(fù)制

發(fā)布時間:2020-06-14 19:07:05 來源:網(wǎng)絡(luò) 閱讀:391 作者:Java筆記丶 欄目:編程語言

Redis深度歷險分為兩個部分,單機(jī)Redis和分布式Redis。

本文為分布式Redis深度歷險系列的第一篇,主要內(nèi)容為Redis的復(fù)制功能。

Redis的復(fù)制功能的作用和大多數(shù)分布式存儲系統(tǒng)一樣,就是為了支持主從設(shè)計,主從設(shè)計的好處有以下幾點:

  • 讀寫分離,提高讀寫性能

  • 數(shù)據(jù)備份,減少數(shù)據(jù)丟失的風(fēng)險

  • 高可用,避免單點故障

分布式Redis深度歷險-復(fù)制

舊版復(fù)制實現(xiàn)

Redis的復(fù)制主要分為同步和命令傳播兩個步驟:

同步可以理解為全量,是將主服務(wù)器某一時刻的所有數(shù)據(jù)全部同步到從服務(wù)器。

命令傳播可以理解為增量,當(dāng)主服務(wù)器數(shù)據(jù)被修改時,主服務(wù)器向從服務(wù)器發(fā)送對應(yīng)的數(shù)據(jù)修改命令。

?

同步

同步分為以下幾個步驟:

1.從服務(wù)器向主服務(wù)器發(fā)送SYNC命令(執(zhí)行SLAVE OF命令的第一步也會執(zhí)行SYNC

2.主服務(wù)器在收到從服務(wù)器命令時,會執(zhí)行BGSAVE,也就是新開一個子進(jìn)程將內(nèi)存中的數(shù)據(jù)保存到RDB文件中。同時使用一個內(nèi)存緩沖區(qū)記錄從現(xiàn)在開始執(zhí)行的寫命令,該內(nèi)存緩沖區(qū)的作用就是記錄RDB文件生成期間的增量。

3.向從服務(wù)器發(fā)送RDB文件

4.將緩沖區(qū)中的寫命令發(fā)送給從服務(wù)器

同步可以分為兩種情況,一種是從服務(wù)器第一次連接主服務(wù)器,另一種是從服務(wù)與主服務(wù)器的網(wǎng)絡(luò)鏈接斷開了,重新連上主服務(wù)器并重新同步。

?

命令傳播

命令傳播實現(xiàn)邏輯比較簡單,當(dāng)主服務(wù)器執(zhí)行了寫命令后,為了保證從服務(wù)器與主服務(wù)器數(shù)據(jù)的一致性,主服務(wù)器會將寫命令發(fā)送給從服務(wù)器,從服務(wù)器執(zhí)行完收到的寫命令后其數(shù)據(jù)就能和主服務(wù)器保持一致了(當(dāng)然會有延時),注意,從服務(wù)器對于客戶端來說是只讀的,因此從服務(wù)器的所有數(shù)據(jù)都是來自于主服務(wù)器的同步or命令傳播。

?

舊版復(fù)制存在的問題

假設(shè)Redis主從服務(wù)器之間的網(wǎng)絡(luò)環(huán)境不太可靠,我們來看看上述復(fù)制方法會出現(xiàn)什么問題。假設(shè)有主服務(wù)器A和從服務(wù)器B,主服務(wù)器中目前存在1-10000共一萬條數(shù)據(jù)。

1.初始連接,從服務(wù)器第一次從主服務(wù)器同步數(shù)據(jù),同步完成后,從服務(wù)器也有1-10000共一萬條數(shù)據(jù)。

2.主服務(wù)器新增10001,10002兩條數(shù)據(jù)

3.通過命令傳播,從服務(wù)器也新增10001,10002兩條數(shù)據(jù)

4.這時候主從服務(wù)器之間的網(wǎng)絡(luò)斷開

5.主服務(wù)器新增數(shù)據(jù)10003,因為網(wǎng)絡(luò)斷開,所以從服務(wù)器感受不到數(shù)據(jù)變化

6.網(wǎng)絡(luò)恢復(fù),從服務(wù)器重新連接上主服務(wù)器,并發(fā)送SYNC命令,進(jìn)行同步操作

7.主服務(wù)器將所有數(shù)據(jù)發(fā)送給從服務(wù)器(1-10003)

從上述步驟中可以看到,當(dāng)從服務(wù)器重新連接上主服務(wù)器時,會重新進(jìn)行全量同步,造成大量不必要的IO開銷,如果網(wǎng)絡(luò)環(huán)境不穩(wěn)定時,會導(dǎo)致主服務(wù)器一直將內(nèi)存中的數(shù)據(jù)寫到磁盤再發(fā)送給從服務(wù)器。

?

新版復(fù)制實現(xiàn)

為了解決老版復(fù)制問題,Redis2.8對于復(fù)制功能進(jìn)行了優(yōu)化。實現(xiàn)如下:

1.主服務(wù)器會維護(hù)一個偏移量,每次向服務(wù)器傳播N個字節(jié)的數(shù)據(jù)時,該偏移量就會加上N,比如說一開始是0,接受到一條set key1 value1后,其偏移量就為13(真實偏移可能不是13,只是舉個例子)。//這里可能要看下代碼確認(rèn)

2.從服務(wù)器也維護(hù)一個偏移量,當(dāng)從服務(wù)器收到到主服務(wù)器的N個字節(jié)數(shù)據(jù)時,該偏移量會加上N。

3.主服務(wù)器維護(hù)一個固定大小的緩沖區(qū),每次接受到客戶端寫命令后,都會將對應(yīng)命令往這個緩沖區(qū)寫入。當(dāng)寫入內(nèi)容超出固定大小后,會覆蓋原來的數(shù)據(jù)。

4.主服務(wù)器有一個唯一id

5.從服務(wù)器連接上主服務(wù)時,會向主服務(wù)器發(fā)送上一次連接的主服務(wù)器的id以及偏移量,這里又分幾種情況:

  1. 如果從服務(wù)器沒傳id或者id與當(dāng)前主服務(wù)器不匹配,那主服務(wù)器將傳送全量數(shù)據(jù)

  2. 如果從服務(wù)器的offset在緩沖區(qū)中不能找到(落后太多導(dǎo)致緩沖區(qū)已經(jīng)被新數(shù)據(jù)覆蓋了),那也會進(jìn)行全量同步

  3. 如果offset能在緩沖區(qū)找到,則主服務(wù)從offset開始,將緩沖區(qū)的數(shù)據(jù)依次發(fā)送給從服務(wù)器。(有做pipeline的優(yōu)化嗎)

以上就是新版復(fù)制的大致思路,要注意的是,主服務(wù)器緩沖區(qū)的大小設(shè)置很關(guān)鍵,如果設(shè)置的太大會導(dǎo)致空間浪費,如果太小會導(dǎo)致網(wǎng)絡(luò)環(huán)境不好時,其退化為老版復(fù)制。

之前我就踩過這樣的坑:在上云時,redis集群在兩個不同機(jī)房,主從之前網(wǎng)絡(luò)環(huán)境不太穩(wěn)定,而redis機(jī)器上存儲的value比較大,很容易就將緩沖區(qū)占滿導(dǎo)致每次全量同步,形成惡性循環(huán),從服務(wù)器落后不可讀,主服務(wù)器不可寫(當(dāng)從Redis落后太多時,主Redis將拒絕寫入,具體參數(shù)可以配置的,下文還會提到)

所以建議將緩沖區(qū)大小設(shè)置為平均重連間隔*每秒寫入數(shù)據(jù)量*2

?

主從心跳機(jī)制

從服務(wù)器默認(rèn)會每秒一次的頻率向主服務(wù)器發(fā)送心跳:
REPLCONF A?K <replication_offset>,
replication_offset代表從服務(wù)器當(dāng)前的復(fù)制偏移量。

心跳有三個作用:

1.檢測主從服務(wù)器的網(wǎng)絡(luò)連接

2.實現(xiàn)min-slaves功能

3.檢測命令丟失

?

檢測主從服務(wù)器的網(wǎng)絡(luò)連接

主服務(wù)器會記錄從服務(wù)器上次發(fā)送心跳是什么時間,根據(jù)這個時間,我們能知道主從服務(wù)器之間的連接是不是出現(xiàn)了故障

?

實現(xiàn)min-slaves功能

Redis為了保證數(shù)據(jù)的安全性,可以配置當(dāng)從服務(wù)器小于min-slaves-to-write個或者min-slaves-to-write個從服務(wù)器的延遲都大于等于min-slaves-max-lag時,主服務(wù)器拒絕寫。

?

檢測命令丟失

主從之間的復(fù)制,其實是以主服務(wù)器作為從服務(wù)器的客戶端來實現(xiàn)的(在Redis中,所有服務(wù)器之間的數(shù)據(jù)傳遞都是以該種方式)。假設(shè)主服務(wù)器向從服務(wù)器發(fā)送一條寫命令,但網(wǎng)絡(luò)出現(xiàn)異常,從服務(wù)器并沒有收到該命令。


這就會導(dǎo)致數(shù)據(jù)不一致的狀態(tài)(你可能想主服務(wù)器發(fā)送命令時,如果從沒返回失敗,進(jìn)行重發(fā)不就好了嗎?如果說從成功執(zhí)行了命令,但是再回復(fù)主的時候出現(xiàn)了問題,那主如果重發(fā)就會造成數(shù)據(jù)異常了)。所以主服務(wù)器會根據(jù)心跳信息來決定要發(fā)送的數(shù)據(jù)??磦€例子:

初始,主服務(wù)器和從服務(wù)器偏移量都是100。

主服務(wù)器收到客戶端的寫命令,將偏移量改成110,同時向從服務(wù)器發(fā)送寫命令,但因網(wǎng)絡(luò)原因,從服務(wù)器并沒有收到,其偏移量仍然是100。主服務(wù)器根據(jù)心跳發(fā)現(xiàn)從服務(wù)器的偏移量是100落后于自己,所以會將100-110的數(shù)據(jù)進(jìn)行重發(fā)。

?

看到這里,你可能對于上述方案的正確性感到質(zhì)疑:在從服務(wù)器接收到100-110的數(shù)據(jù)前,它發(fā)送心跳包告訴主服務(wù)器自己當(dāng)前偏移為100,然后接收到了100-110的數(shù)據(jù)。這時下個心跳還沒發(fā)出,主服務(wù)器認(rèn)為從服務(wù)器落后于自己,再次發(fā)送100-110的數(shù)據(jù),導(dǎo)致從服務(wù)器再次寫入100-110的數(shù)據(jù),導(dǎo)致數(shù)據(jù)異常!

?

如果你有想到這個問題,說明你是有在認(rèn)真思考了~

其實是不存在這種情況的,原因是redis是單線程的!記住單線程三個字,再回頭看一遍問題描述,相信你能想明白~

?


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI