自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Redis的主從庫(kù)如何實(shí)現(xiàn)數(shù)據(jù)一致?

數(shù)據(jù)庫(kù) Redis
我們一同深入研究了Redis的主從復(fù)制同步原理,總結(jié)來(lái)說(shuō),主要有三種同步模式:全量復(fù)制、基于長(zhǎng)連接的命令傳播,以及增量復(fù)制。?

之前我們?cè)敿?xì)了解了 Redis 的持久化機(jī)制,包括 AOF 和 RDB,它們能在宕機(jī)發(fā)生時(shí),盡量少丟失數(shù)據(jù),確??煽啃?。然而,如果只有一個(gè) Redis 實(shí)例在運(yùn)行,它在恢復(fù)數(shù)據(jù)期間將無(wú)法服務(wù)新的數(shù)據(jù)請(qǐng)求,這是一個(gè)可用性上的問(wèn)題。

那么,Redis所謂的高可靠性意味著什么呢?它涵蓋兩個(gè)重要方面:數(shù)據(jù)不輕易丟失和服務(wù)不容易中斷。AOF和RDB確保了前者,但對(duì)于后者,Redis的解決方法是增加冗余副本,將數(shù)據(jù)保存在多個(gè)Redis實(shí)例上。即使其中一個(gè)實(shí)例發(fā)生故障且需要一段時(shí)間來(lái)恢復(fù),其他實(shí)例仍能繼續(xù)提供服務(wù),不會(huì)影響業(yè)務(wù)的正常運(yùn)行。

然而,多個(gè)實(shí)例存儲(chǔ)相同的數(shù)據(jù)引發(fā)了一個(gè)新的問(wèn)題:如何保持這些數(shù)據(jù)副本的一致性?難道需要將數(shù)據(jù)讀寫操作同時(shí)傳遞給所有實(shí)例嗎?

Redis采用主從庫(kù)的模式來(lái)確保數(shù)據(jù)副本的一致性。這種模式允許主庫(kù)(master)處理寫操作和主動(dòng)廣播數(shù)據(jù)變更給從庫(kù)(slave),而從庫(kù)則主要用于讀操作。這種讀寫分離的方法有助于保持?jǐn)?shù)據(jù)的一致性,同時(shí)提高了性能和可用性。

讀操作:主庫(kù)、從庫(kù)都可以接收;

寫操作:首先到主庫(kù)執(zhí)行,然后,主庫(kù)將寫操作同步給從庫(kù)。

Redis主從庫(kù)和讀寫分離Redis主從庫(kù)和讀寫分離

那么,為什么要采用讀寫分離的方式呢?

你可以設(shè)想一下,如果在上圖中,不管是主庫(kù)還是從庫(kù),都能接收客戶端的寫操作,那么,一個(gè)直接的問(wèn)題就是:如果客戶端對(duì)同一個(gè)數(shù)據(jù)(例如 k1)前后修改了三次,每一次的修改請(qǐng)求都發(fā)送到不同的實(shí)例上,在不同的實(shí)例上執(zhí)行,那么,這個(gè)數(shù)據(jù)在這三個(gè)實(shí)例上的副本就不一致了(分別是 v1、v2 和 v3)。在讀取這個(gè)數(shù)據(jù)的時(shí)候,就可能讀取到舊的值。

如果我們堅(jiān)持要保持這個(gè)數(shù)據(jù)在三個(gè)實(shí)例上一致,那么就需要引入諸如加鎖、實(shí)例間協(xié)商是否完成修改等一系列復(fù)雜操作。這些額外的操作會(huì)帶來(lái)巨大的性能和復(fù)雜性開銷,通常是難以接受的。

然而,一旦采用主從庫(kù)模式并啟用讀寫分離,數(shù)據(jù)的修改操作只會(huì)在主庫(kù)上進(jìn)行。主庫(kù)獲得最新數(shù)據(jù)后,會(huì)將其同步給從庫(kù)。這樣,主從庫(kù)之間的數(shù)據(jù)就能保持一致。

你可能會(huì)想知道,主從庫(kù)同步是一次性傳輸所有數(shù)據(jù),還是分批次同步?如果主從庫(kù)之間的網(wǎng)絡(luò)連接斷開了,數(shù)據(jù)如何保持一致?在這節(jié)課中,我將與你分享主從庫(kù)同步的工作原理,以及如何應(yīng)對(duì)網(wǎng)絡(luò)中斷等風(fēng)險(xiǎn)的解決方案。

好的,首先,讓我們來(lái)了解主從庫(kù)之間的首次同步是如何進(jìn)行的,這是 Redis 實(shí)例建立主從庫(kù)模式后的初始步驟。

主從庫(kù)間如何進(jìn)行第一次同步?

當(dāng)我們啟動(dòng)多個(gè) Redis 實(shí)例的時(shí)候,它們相互之間就可以通過(guò) 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

接下來(lái),我們就要學(xué)習(xí)主從庫(kù)間數(shù)據(jù)第一次同步的三個(gè)階段了。你可以先看一下下面這張圖,有個(gè)整體感知,接下來(lái)我再具體介紹。

主從庫(kù)第一次同步的流程主從庫(kù)第一次同步的流程

第一階段是主從庫(kù)間建立連接、協(xié)商同步的過(guò)程,主要是為全量復(fù)制做準(zhǔn)備。在這一步,從庫(kù)和主庫(kù)建立起連接,并告訴主庫(kù)即將進(jìn)行同步,主庫(kù)確認(rèn)回復(fù)后,主從庫(kù)間就可以開始同步了

具體來(lái)說(shuō),從庫(kù)給主庫(kù)發(fā)送 psync 命令,表示要進(jìn)行數(shù)據(jù)同步,主庫(kù)根據(jù)這個(gè)命令的參數(shù)來(lái)啟動(dòng)復(fù)制。psync 命令包含了主庫(kù)的 runID 和復(fù)制進(jìn)度 offset 兩個(gè)參數(shù)。

runID,是每個(gè) Redis 實(shí)例啟動(dòng)時(shí)都會(huì)自動(dòng)生成的一個(gè)隨機(jī) ID,用來(lái)唯一標(biāo)記這個(gè)實(shí)例。當(dāng)從庫(kù)和主庫(kù)第一次復(fù)制時(shí),因?yàn)椴恢乐鲙?kù)的 runID,所以將 runID 設(shè)為“?”。

offset,此時(shí)設(shè)為 -1,表示第一次復(fù)制。

主庫(kù)收到 psync 命令后,會(huì)用 FULLRESYNC 響應(yīng)命令帶上兩個(gè)參數(shù):主庫(kù) runID 和主庫(kù)目前的復(fù)制進(jìn)度 offset,返回給從庫(kù)。從庫(kù)收到響應(yīng)后,會(huì)記錄下這兩個(gè)參數(shù)。

這里有個(gè)地方需要注意,FULLRESYNC 響應(yīng)表示第一次復(fù)制采用的全量復(fù)制,也就是說(shuō),主庫(kù)會(huì)把當(dāng)前所有的數(shù)據(jù)都復(fù)制給從庫(kù)

在第二階段,主庫(kù)將所有數(shù)據(jù)同步給從庫(kù)。從庫(kù)收到數(shù)據(jù)后,在本地完成數(shù)據(jù)加載。這個(gè)過(guò)程依賴于內(nèi)存快照生成的 RDB 文件。

具體而言,主庫(kù)首先執(zhí)行 bgsave 命令,生成 RDB 文件,然后將這個(gè)文件發(fā)送給從庫(kù)。一旦從庫(kù)接收到 RDB 文件,它會(huì)首先清空當(dāng)前數(shù)據(jù)庫(kù),然后加載這個(gè) RDB 文件。這是因?yàn)樵趶膸?kù)通過(guò) replicaof 命令開始和主庫(kù)同步之前,可能已經(jīng)存在其他數(shù)據(jù)。為了確保新數(shù)據(jù)的一致性,從庫(kù)必須清空當(dāng)前數(shù)據(jù)庫(kù)。

在主庫(kù)將數(shù)據(jù)同步給從庫(kù)的過(guò)程中,主庫(kù)不會(huì)被阻塞,繼續(xù)正常處理請(qǐng)求。這是非常重要的,因?yàn)樽枞鲙?kù)將導(dǎo)致 Redis 服務(wù)中斷。然而,需要注意的是,這些請(qǐng)求中的寫操作并沒(méi)有記錄到剛剛生成的 RDB 文件中。為了確保主從庫(kù)的數(shù)據(jù)一致性,主庫(kù)會(huì)在內(nèi)存中使用專門的復(fù)制緩沖區(qū),記錄在生成 RDB 文件后接收到的所有寫操作。

最后,我們來(lái)到第三個(gè)階段,主庫(kù)將第二階段執(zhí)行期間接收的新寫命令發(fā)送給從庫(kù)。具體來(lái)說(shuō),一旦主庫(kù)完成 RDB 文件的發(fā)送,它會(huì)將此刻復(fù)制緩沖區(qū)中的修改操作發(fā)送給從庫(kù)。從庫(kù)將再次執(zhí)行這些操作,從而實(shí)現(xiàn)主從庫(kù)的數(shù)據(jù)同步。這三個(gè)階段將主庫(kù)和從庫(kù)的數(shù)據(jù)保持一致。

主從級(jí)聯(lián)模式分擔(dān)全量復(fù)制時(shí)的主庫(kù)壓力

通過(guò)分析主從庫(kù)間第一次數(shù)據(jù)同步的過(guò)程,你可以看到,一次全量復(fù)制中,對(duì)于主庫(kù)來(lái)說(shuō),需要完成兩個(gè)耗時(shí)的操作:生成 RDB 文件和傳輸 RDB 文件。

如果從庫(kù)數(shù)量很多,而且都要和主庫(kù)進(jìn)行全量復(fù)制的話,就會(huì)導(dǎo)致主庫(kù)忙于 fork 子進(jìn)程生成 RDB 文件,進(jìn)行數(shù)據(jù)全量同步。fork 這個(gè)操作會(huì)阻塞主線程處理正常請(qǐng)求,從而導(dǎo)致主庫(kù)響應(yīng)應(yīng)用程序的請(qǐng)求速度變慢。此外,傳輸 RDB 文件也會(huì)占用主庫(kù)的網(wǎng)絡(luò)帶寬,同樣會(huì)給主庫(kù)的資源使用帶來(lái)壓力。那么,有沒(méi)有好的解決方法可以分擔(dān)主庫(kù)壓力呢?

其實(shí)是有的,這就是“主 - 從 - 從”模式。

在剛才介紹的主從庫(kù)模式中,所有的從庫(kù)都是和主庫(kù)連接,所有的全量復(fù)制也都是和主庫(kù)進(jìn)行的?,F(xiàn)在,我們可以通過(guò)“主 - 從 - 從”模式將主庫(kù)生成 RDB 和傳輸 RDB 的壓力,以級(jí)聯(lián)的方式分散到從庫(kù)上。

簡(jiǎn)單來(lái)說(shuō),我們?cè)诓渴鹬鲝募旱臅r(shí)候,可以手動(dòng)選擇一個(gè)從庫(kù)(比如選擇內(nèi)存資源配置較高的從庫(kù)),用于級(jí)聯(lián)其他的從庫(kù)。然后,我們可以再選擇一些從庫(kù)(例如三分之一的從庫(kù)),在這些從庫(kù)上執(zhí)行如下命令,讓它們和剛才所選的從庫(kù),建立起主從關(guān)系。

replicaof  所選從庫(kù)的IP 6379

這樣一來(lái),這些從庫(kù)就會(huì)知道,在進(jìn)行同步時(shí),不用再和主庫(kù)進(jìn)行交互了,只要和級(jí)聯(lián)的從庫(kù)進(jìn)行寫操作同步就行了,這就可以減輕主庫(kù)上的壓力,如下圖所示:

級(jí)聯(lián)的“主-從-從”模式級(jí)聯(lián)的“主-從-從”模式

好了,到這里,我們了解了主從庫(kù)間通過(guò)全量復(fù)制實(shí)現(xiàn)數(shù)據(jù)同步的過(guò)程,以及通過(guò)“主 - 從 - 從”模式分擔(dān)主庫(kù)壓力的方式。那么,一旦主從庫(kù)完成了全量復(fù)制,它們之間就會(huì)一直維護(hù)一個(gè)網(wǎng)絡(luò)連接,主庫(kù)會(huì)通過(guò)這個(gè)連接將后續(xù)陸續(xù)收到的命令操作再同步給從庫(kù),這個(gè)過(guò)程也稱為基于長(zhǎng)連接的命令傳播,可以避免頻繁建立連接的開銷。

聽上去好像很簡(jiǎn)單,但不可忽視的是,這個(gè)過(guò)程中存在著風(fēng)險(xiǎn)點(diǎn),最常見的就是網(wǎng)絡(luò)斷連或阻塞。如果網(wǎng)絡(luò)斷連,主從庫(kù)之間就無(wú)法進(jìn)行命令傳播了,從庫(kù)的數(shù)據(jù)自然也就沒(méi)辦法和主庫(kù)保持一致了,客戶端就可能從從庫(kù)讀到舊數(shù)據(jù)。

接下來(lái),我們就來(lái)聊聊網(wǎng)絡(luò)斷連后的解決辦法。

主從庫(kù)間網(wǎng)絡(luò)斷了怎么辦?

在Redis 2.8之前,主從復(fù)制中若出現(xiàn)網(wǎng)絡(luò)閃斷,從庫(kù)將不得不重新執(zhí)行一次繁重的全量復(fù)制操作,這勢(shì)必造成巨大的開銷。

不過(guò),Redis自2.8版本開始,出現(xiàn)網(wǎng)絡(luò)斷連時(shí),主從庫(kù)采用一種稱為"增量復(fù)制"的方式來(lái)繼續(xù)同步。從名字中我們可以猜測(cè),它與全量復(fù)制有所不同:全量復(fù)制同步所有數(shù)據(jù),而增量復(fù)制僅將主庫(kù)在網(wǎng)絡(luò)斷連期間接收到的命令同步給從庫(kù)。

那么,在增量復(fù)制過(guò)程中,主從庫(kù)是如何確保同步的呢?關(guān)鍵在于repl_backlog_buffer這個(gè)緩沖區(qū)。現(xiàn)在,我們來(lái)詳細(xì)看看它如何在增量命令同步中發(fā)揮作用。

當(dāng)主從庫(kù)出現(xiàn)斷連后,主庫(kù)會(huì)將在此期間接收到的寫操作命令記錄在復(fù)制緩沖區(qū)(replication buffer)中,并同時(shí)寫入repl_backlog_buffer這個(gè)緩沖區(qū)。

repl_backlog_buffer 是一個(gè)環(huán)形緩沖區(qū),主庫(kù)會(huì)記錄自己寫到的位置,從庫(kù)則會(huì)記錄自己已經(jīng)讀到的位置。

一開始,主庫(kù)和從庫(kù)的寫讀位置是相同的,可以看作它們的初始位置。隨著主庫(kù)接收新的寫操作,主庫(kù)在緩沖區(qū)中的寫位置會(huì)逐漸偏離起始位置。通常,我們使用偏移量來(lái)度量這個(gè)偏移距離,對(duì)于主庫(kù),這個(gè)偏移量就是master_repl_offset。隨著主庫(kù)接收的新寫操作增多,這個(gè)偏移量也會(huì)逐漸增加。

同樣的,從庫(kù)在復(fù)制完寫操作命令后,其在緩沖區(qū)中的讀位置也開始逐漸偏離初始位置。此時(shí),從庫(kù)已經(jīng)復(fù)制的偏移量slave_repl_offset也在不斷增加。在正常情況下,這兩個(gè)偏移量基本上是相等的。

Redis repl_backlog_buffer的使用Redis repl_backlog_buffer的使用

主從庫(kù)的連接恢復(fù)之后,從庫(kù)首先會(huì)給主庫(kù)發(fā)送 psync 命令,并把自己當(dāng)前的 slave_repl_offset 發(fā)給主庫(kù),主庫(kù)會(huì)判斷自己的 master_repl_offset 和 slave_repl_offset 之間的差距。

在網(wǎng)絡(luò)斷連階段,主庫(kù)可能會(huì)收到新的寫操作命令,所以,一般來(lái)說(shuō),master_repl_offset 會(huì)大于 slave_repl_offset。此時(shí),主庫(kù)只用把 master_repl_offset 和 slave_repl_offset 之間的命令操作同步給從庫(kù)就行。

就像剛剛示意圖的中間部分,主庫(kù)和從庫(kù)之間相差了 put d e 和 put d f 兩個(gè)操作,在增量復(fù)制時(shí),主庫(kù)只需要把它們同步給從庫(kù),就行了。

說(shuō)到這里,我們?cè)俳柚粡垐D,回顧下增量復(fù)制的流程。

Redis增量復(fù)制流程Redis增量復(fù)制流程

不過(guò),有一個(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ù)不一致。

因此,我們要想辦法避免這一情況,一般而言,我們可以調(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 的最終值。

舉個(gè)例子,如果主庫(kù)每秒寫入 2000 個(gè)操作,每個(gè)操作的大小為 2KB,網(wǎng)絡(luò)每秒能傳輸 1000 個(gè)操作,那么,有 1000 個(gè)操作需要緩沖起來(lái),這就至少需要 2MB 的緩沖空間。否則,新寫的命令就會(huì)覆蓋掉舊操作了。為了應(yīng)對(duì)可能的突發(fā)壓力,我們最終把 repl_backlog_size 設(shè)為 4MB。

這樣一來(lái),增量復(fù)制時(shí)主從庫(kù)的數(shù)據(jù)不一致風(fēng)險(xiǎn)就降低了。不過(guò),如果并發(fā)請(qǐng)求量非常大,連兩倍的緩沖空間都存不下新操作請(qǐng)求的話,此時(shí),主從庫(kù)數(shù)據(jù)仍然可能不一致。

針對(duì)這種情況,一方面,你可以根據(jù) Redis 所在服務(wù)器的內(nèi)存資源再適當(dāng)增加 repl_backlog_size 值,比如說(shuō)設(shè)置成緩沖空間大小的 4 倍,另一方面,你可以考慮使用切片集群來(lái)分擔(dān)單個(gè)主庫(kù)的請(qǐng)求壓力。關(guān)于切片集群,我會(huì)在第 9 講具體介紹。

小結(jié)

我們一同深入研究了Redis的主從復(fù)制同步原理,總結(jié)來(lái)說(shuō),主要有三種同步模式:全量復(fù)制、基于長(zhǎng)連接的命令傳播,以及增量復(fù)制。

全量復(fù)制雖然可能耗費(fèi)時(shí)間,但對(duì)于從庫(kù)來(lái)說(shuō),在進(jìn)行首次同步時(shí),全量復(fù)制是不可避免的。因此,這里給出一個(gè)小建議:一個(gè)Redis實(shí)例的數(shù)據(jù)庫(kù)不應(yīng)過(guò)于龐大,最好將其保持在幾GB的規(guī)模。這有助于減少RDB文件的生成、傳輸和重新加載所帶來(lái)的成本。此外,為了減輕主庫(kù)的同步壓力,以及避免多個(gè)從庫(kù)同時(shí)進(jìn)行全量復(fù)制,可以采用"主-從-從"級(jí)聯(lián)同步模式。

長(zhǎng)連接復(fù)制是主從復(fù)制在正常運(yùn)行時(shí)采用的常規(guī)同步方式。在這個(gè)階段,主庫(kù)和從庫(kù)之間通過(guò)命令傳播來(lái)保持同步。然而,如果在此過(guò)程中出現(xiàn)了網(wǎng)絡(luò)斷連,增量復(fù)制就變得至關(guān)重要。我特別提醒注意repl_backlog_size配置參數(shù)。如果它設(shè)置得太小,在增量復(fù)制時(shí),可能導(dǎo)致從庫(kù)的同步進(jìn)度跟不上主庫(kù),最終導(dǎo)致從庫(kù)需要重新進(jìn)行全量復(fù)制。通過(guò)增大這個(gè)參數(shù)的值,可以降低從庫(kù)在網(wǎng)絡(luò)斷連時(shí)重新進(jìn)行全量復(fù)制的風(fēng)險(xiǎn)。

然而,采用主從復(fù)制模式中的讀寫分離雖然能夠避免多個(gè)實(shí)例同時(shí)寫入引發(fā)的數(shù)據(jù)不一致問(wèn)題,但仍然面臨主庫(kù)故障的潛在風(fēng)險(xiǎn)。如果主庫(kù)出現(xiàn)故障,從庫(kù)將如何應(yīng)對(duì)?數(shù)據(jù)是否能夠保持一致?Redis是否能夠正常提供服務(wù)?在接下來(lái)的兩節(jié)課中,我們將深入探討在主庫(kù)故障后如何確保服務(wù)的可靠性。

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)本農(nóng)
相關(guān)推薦

2022-02-23 09:17:09

數(shù)據(jù)庫(kù)分離變更

2018-07-08 07:38:28

數(shù)據(jù)庫(kù)緩存數(shù)據(jù)

2020-07-20 14:06:38

數(shù)據(jù)庫(kù)主從同步服務(wù)

2024-08-20 16:13:52

2021-12-14 07:15:57

MySQLRedis數(shù)據(jù)

2022-09-15 10:37:46

MySQLRedis數(shù)據(jù)一致性

2025-04-27 08:52:21

Redis數(shù)據(jù)庫(kù)緩存

2021-12-30 09:32:04

緩存數(shù)據(jù)庫(kù)數(shù)據(jù)

2024-05-11 07:37:43

數(shù)據(jù)Redis策略

2022-02-17 21:04:27

數(shù)據(jù)庫(kù)MysqlRedis

2023-05-26 07:34:50

RedisMySQL緩存

2024-10-28 12:41:25

2022-03-29 10:39:10

緩存數(shù)據(jù)庫(kù)數(shù)據(jù)

2025-04-03 09:51:37

2024-01-31 09:54:51

Redis分布式

2020-11-17 06:42:21

MySQL數(shù)據(jù)庫(kù)開源

2023-09-24 14:35:43

Redis數(shù)據(jù)庫(kù)

2022-03-16 15:54:52

MySQL數(shù)據(jù)format

2024-05-28 00:50:00

RedisMySQL緩存

2019-08-30 12:46:10

并發(fā)扣款查詢SQL
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)