之前總結(jié)過redis的持久化機(jī)制:深度剖析Redis持久化機(jī)制,持久化機(jī)制主要解決redis數(shù)據(jù)單機(jī)備份問題;redis的高可用需要考慮數(shù)據(jù)的多機(jī)備份,多機(jī)備份通過主從復(fù)制來(lái)實(shí)現(xiàn),這是redis高可用的基石。本文將詳細(xì)介紹redis主從復(fù)制的實(shí)現(xiàn)原理,在使用過程中應(yīng)該注意的問題和相關(guān)配置。
1. CAP理論
CAP理論是分布式領(lǐng)域的牛頓定律,所有的分布式存儲(chǔ)中間件都要使用它作為理論基石。如下圖所示:
這個(gè)原理很簡(jiǎn)單,首先明確幾個(gè)概念:
C : Consistent, 一致性
A : Availability, 可用性
P : Partition tolerance, 分區(qū)容忍性
分布式系統(tǒng)的節(jié)點(diǎn)往往分布在不同的機(jī)器上,它們之間由網(wǎng)絡(luò)進(jìn)行隔離,當(dāng)網(wǎng)絡(luò)斷開時(shí)就會(huì)產(chǎn)生網(wǎng)絡(luò)分區(qū)。網(wǎng)絡(luò)分區(qū)不可避免,但是當(dāng)網(wǎng)絡(luò)分區(qū)發(fā)生時(shí),對(duì)分布式系統(tǒng)中一個(gè)節(jié)點(diǎn)的修改操作無(wú)法同步給其它節(jié)點(diǎn),數(shù)據(jù)一致性也就無(wú)法滿足;想要滿足一致性,除非犧牲可用性,也就是暫停分布式節(jié)點(diǎn)服務(wù),等到網(wǎng)絡(luò)恢復(fù),數(shù)據(jù)一致后,再對(duì)外提供服務(wù)。分布式系統(tǒng)中網(wǎng)絡(luò)分區(qū)不可避免,一致性和可用性水火不容。這就是cap理論:網(wǎng)絡(luò)分區(qū)發(fā)生時(shí),一致性和可用性兩難全 。
基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
視頻教程:https://doc.iocoder.cn/video/
2. redis主從復(fù)制
2.1 概述
互聯(lián)網(wǎng)圈經(jīng)常談“三高”架構(gòu):高并發(fā)、高性能、高可用 。對(duì)于redis來(lái)說(shuō),高并發(fā)、高性能可以保證,高可用需要架構(gòu)的設(shè)計(jì),單機(jī)redis由于存在系統(tǒng)崩潰、硬盤故障的風(fēng)險(xiǎn),還有內(nèi)存的限制,所以企業(yè)一般都會(huì)搭建主從,對(duì)系統(tǒng)有更高要求會(huì)搭建集群。
為了保持?jǐn)?shù)據(jù)一致性,主節(jié)點(diǎn)(master)只寫,從節(jié)點(diǎn)(slave)只讀,數(shù)據(jù)由主節(jié)點(diǎn)復(fù)制給從節(jié)點(diǎn),這個(gè)復(fù)制的過程就是主從復(fù)制 。
redis的主從復(fù)制是異步的,分布式的redis系統(tǒng)并不滿足一致性要求,但是在網(wǎng)絡(luò)斷開的情況下,主節(jié)點(diǎn)依然可以對(duì)外提供服務(wù),滿足可用性。redis保證最終一致性,從節(jié)點(diǎn)會(huì)努力追趕主節(jié)點(diǎn),最終從節(jié)點(diǎn)的狀態(tài)會(huì)和從節(jié)點(diǎn)保持一致。網(wǎng)絡(luò)斷開的情況下,主從節(jié)點(diǎn)數(shù)據(jù)會(huì)出現(xiàn)大量的不一致,但一旦網(wǎng)絡(luò)恢復(fù),從節(jié)點(diǎn)會(huì)繼續(xù)追趕主節(jié)點(diǎn),最終達(dá)到和主節(jié)點(diǎn)狀態(tài)一致。
為了減輕redis主節(jié)點(diǎn)的同步負(fù)擔(dān),redis 的后續(xù)版本還增加了從從同步,與此同時(shí),數(shù)據(jù)一致性會(huì)變差。
2.2 主從復(fù)制的作用
主從復(fù)制在服務(wù)中起到了什么效果呢?
讀寫分離:master寫,slave讀,提高服務(wù)器的讀寫負(fù)載能力。
負(fù)載均衡:基于主從架構(gòu),配合讀寫分離,由slave分擔(dān)master負(fù)載,并根據(jù)需求的變化,改變slave的數(shù)量,通過多個(gè)從節(jié)點(diǎn)分擔(dān)數(shù)據(jù)讀取負(fù)載,大大提高redis服務(wù)器并發(fā)量和數(shù)據(jù)吞吐量。
故障恢復(fù):當(dāng)master出現(xiàn)問題時(shí),由slave提供服務(wù),實(shí)現(xiàn)快速的故障恢復(fù)。
數(shù)據(jù)冗余:實(shí)現(xiàn)數(shù)據(jù)熱備份,是持久化之外的一種數(shù)據(jù)冗余方式。
高可用基石:基于主從復(fù)制,構(gòu)建哨兵模式與集群,實(shí)現(xiàn)redis的高可用方案。
2.3 怎樣配置實(shí)現(xiàn)主從復(fù)制?
有三種配置實(shí)現(xiàn)redis的主從:
方式一:客戶端發(fā)送命令
slaveof??
方式二:?jiǎn)?dòng)服務(wù)器時(shí)添加參數(shù)
redis-server?-slaveof?
方式三:服務(wù)器配置文件中配置(通過redis.conf)
slaveof?
比較主流的用法是通過配置文件的方式實(shí)現(xiàn)主從。還有其它的一些命令:
主從斷開連接,可以從客戶端發(fā)送命令
slaveof?no?one
服務(wù)端設(shè)置了授權(quán)訪問
#master有兩種方式設(shè)置
//--?1.master配置文件中設(shè)置 requirepass?//--?2.master客戶端發(fā)送命令設(shè)置密碼 config?set?requirepass? config?get?requirepass #slave有三種方式實(shí)現(xiàn)認(rèn)證 //--?1.客戶端發(fā)送命令設(shè)置密碼 auth? //--?2.slave配置文件設(shè)置密碼 masterauth? //--?3.啟動(dòng)客戶端設(shè)置密碼 redis-cli?-a?
2.4 redis主從復(fù)制的工作流程
redis主從復(fù)制實(shí)現(xiàn)過程有三個(gè)階段:
建立連接、數(shù)據(jù)同步、命令傳播。建立連接階段主從節(jié)點(diǎn)建立通信的橋梁,彼此之間同步一些基礎(chǔ)信息;數(shù)據(jù)同步階段實(shí)現(xiàn)從節(jié)點(diǎn)全量同步主節(jié)點(diǎn)的數(shù)據(jù);從節(jié)點(diǎn)同步完主節(jié)點(diǎn)數(shù)據(jù)之后,就進(jìn)入了命令傳播階段,主節(jié)點(diǎn)接收寫請(qǐng)求,數(shù)據(jù)不斷發(fā)生變化,通過命令傳播階段主節(jié)點(diǎn)將數(shù)據(jù)源源不斷的同步給從節(jié)點(diǎn)。下邊我們?cè)敿?xì)介紹主從復(fù)制這三個(gè)階段的工作細(xì)節(jié)和注意事項(xiàng)。
2.4.1 建立連接階段
建立slave到master的連接,使master能識(shí)別slave, 并保存slave的端口號(hào);與此同時(shí),slave也保存master的地址和端口號(hào)信息。
slave發(fā)送slaveof ip port命令給master,master響應(yīng)slave
slave保存master的ip和端口號(hào),建立socket連接
在socket連接之上,主從節(jié)點(diǎn)實(shí)現(xiàn)了心跳機(jī)制,這部分內(nèi)容也比較重要,后邊會(huì)提到。
如果有認(rèn)證機(jī)制,從節(jié)點(diǎn)通過上邊說(shuō)到的認(rèn)證指令,發(fā)送認(rèn)證信息給master,實(shí)現(xiàn)認(rèn)證。
從節(jié)點(diǎn)將自己的端口信息發(fā)送發(fā)送給主節(jié)點(diǎn),主節(jié)點(diǎn)保存。
通過以上過程主從之間的連接就建立了。
2.4.2 數(shù)據(jù)同步階段
數(shù)據(jù)同步階段實(shí)現(xiàn)的功能是從節(jié)點(diǎn)從主節(jié)點(diǎn)同步全量的數(shù)據(jù)。這個(gè)過程又分為幾個(gè)小階段,最主要的就是數(shù)據(jù)的全量復(fù)制和部分復(fù)制, 對(duì)應(yīng)的流程就是主節(jié)點(diǎn)發(fā)送rdb文件同步數(shù)據(jù)和發(fā)送緩沖區(qū)寫命令(aof)同步數(shù)據(jù)給從節(jié)點(diǎn)。下圖是實(shí)現(xiàn)細(xì)節(jié):
首先slave節(jié)點(diǎn)先發(fā)起命令psync ? -1,向master節(jié)點(diǎn)要全量數(shù)據(jù)。
master節(jié)點(diǎn)接收到指令以后,執(zhí)行bgsave,將當(dāng)前內(nèi)存數(shù)據(jù)快照保存為rdb文件,這個(gè)過程為了不影響主節(jié)點(diǎn)繼續(xù)對(duì)外提供服務(wù),采用了Copy On Write技術(shù)。與此同時(shí),master節(jié)點(diǎn)也會(huì)將bgsave保存快照期間接收到的寫更新命令添加到復(fù)制擠壓緩沖區(qū)當(dāng)中。master節(jié)點(diǎn)rdb文件生成完畢以后,會(huì)通過第一階段建立的socket連接將它發(fā)送給slave節(jié)點(diǎn),還會(huì)發(fā)送+FULLRESYNC runid offset給slave節(jié)點(diǎn),告訴slave節(jié)點(diǎn)自己的runid和offset。
什么是runid?
redis-server在每次啟動(dòng)的時(shí)候都會(huì)生成一個(gè)runid,因?yàn)閞edis-server是一個(gè)守護(hù)進(jìn)程,所以在運(yùn)行期間,runid不會(huì)發(fā)生變化,可以通過info server指令查看runid,它是一個(gè)40位字符長(zhǎng)度的字符串。上文提到的psync有兩個(gè)參數(shù),和+FULLRESYNC一樣:psync
什么是復(fù)制擠壓緩沖區(qū)和offset?
復(fù)制擠壓緩沖區(qū)是一個(gè)先進(jìn)先出(FIFO)的環(huán)形隊(duì)列,用于存儲(chǔ)服務(wù)端執(zhí)行過的命令,每次傳播命令,master節(jié)點(diǎn)都會(huì)將傳播的命令記錄下來(lái),保存在這里。
復(fù)制擠壓緩沖區(qū)由兩部分組成:偏移量和字節(jié)值。字節(jié)值是redis指令字節(jié)的存儲(chǔ)(redis指令以一種Redis序列化文本協(xié)議的格式存儲(chǔ)),偏移量offset就是當(dāng)前字節(jié)值在環(huán)形隊(duì)列中的偏移量。
slave節(jié)點(diǎn)接收完master節(jié)點(diǎn)同步的rdb文件之后,將rdb的內(nèi)容加載到自己的內(nèi)存,然后將master節(jié)點(diǎn)的runid和offset記錄下來(lái)。
有了master節(jié)點(diǎn)的runid和offset,在加載完rdb文件之后,就開始向master節(jié)點(diǎn)發(fā)送新的命令psync runid offset,向master節(jié)點(diǎn)要新數(shù)據(jù)。新數(shù)據(jù)是master節(jié)點(diǎn)在bgsave生成rdb文件時(shí)和向slave同步數(shù)據(jù)的這段時(shí)間產(chǎn)生的,所以這段時(shí)間的工作也稱為部分復(fù)制。
master節(jié)點(diǎn)收到slave節(jié)點(diǎn)發(fā)送的請(qǐng)求數(shù)據(jù)命令之后,會(huì)檢查runid是否一致(是否換主),offset是否一致(因?yàn)閺?fù)制擠壓緩沖區(qū)是定長(zhǎng)的,所有有可能會(huì)溢出),這兩個(gè)條件只要有一個(gè)不滿足,master就會(huì)向slave再次全量的同步數(shù)據(jù)(讀者可能會(huì)發(fā)現(xiàn),如果master節(jié)點(diǎn)寫并發(fā)很高,復(fù)制擠壓緩沖區(qū)又設(shè)置的比較小的話,可能會(huì)每次向slave同步完數(shù)據(jù)以后,每次復(fù)制擠壓緩沖區(qū)都會(huì)溢出,造成主從之間循環(huán)的全量復(fù)制。這確實(shí)是應(yīng)該規(guī)避的問題!我們后邊會(huì)針對(duì)主從復(fù)制應(yīng)該考慮的問題做一個(gè)總結(jié))。在runid和offset都滿足的情況下,master節(jié)點(diǎn)就會(huì)向slave節(jié)點(diǎn)發(fā)送指令+CONTINUE offset,接著從offset位置開始同步數(shù)據(jù),數(shù)據(jù)都在主節(jié)點(diǎn)的復(fù)制擠壓緩沖區(qū)中了,所以直接復(fù)制發(fā)送就可以了。
slave節(jié)點(diǎn)接收到master節(jié)點(diǎn)發(fā)送的+CONTINUE offset指令之后,更新自己保存的offset值,然后將從master節(jié)點(diǎn)同步過來(lái)的數(shù)據(jù),使用bgrewriteaof,重放aof數(shù)據(jù)。
到這里,主從復(fù)制的第二階段:數(shù)據(jù)同步階段工作就完成了。
2.4.3 命令傳播階段
命令傳播階段類似于數(shù)據(jù)同步階段的部分復(fù)制,當(dāng)master節(jié)點(diǎn)數(shù)據(jù)被修改以后,就和slave節(jié)點(diǎn)的數(shù)據(jù)不一致了,這個(gè)時(shí)候master節(jié)點(diǎn)就會(huì)根據(jù)slave上報(bào)的offset開始傳播數(shù)據(jù)(一主多從的架構(gòu)中,master節(jié)點(diǎn)要記錄每一個(gè)slave的offset)。slave接收到數(shù)據(jù)以后,執(zhí)行bgrewriteaof重放數(shù)據(jù)。在這個(gè)工作過程中,如果因?yàn)榫W(wǎng)路問題導(dǎo)致offset溢出或者換主的情況,主從之間還是會(huì)進(jìn)行數(shù)據(jù)的全量同步的。
2.5 心跳機(jī)制
進(jìn)入命令傳播階段以后,master節(jié)點(diǎn)與slave節(jié)點(diǎn)需要進(jìn)行信息傳遞,使用心跳機(jī)制進(jìn)行維護(hù),實(shí)現(xiàn)雙方保持在線。
master節(jié)點(diǎn)心跳使用指令PING,由配置repl-ping-slave-period決定,默認(rèn)10秒,作用是判斷slave是否在線,可以通過info replication獲取slave最后一次連接到現(xiàn)在的時(shí)間間隔,lag的值維護(hù)在0和1視為正常。
slave節(jié)點(diǎn)的心跳任務(wù)使用指令REPLCONF ACK {offset},周期是1秒,slave的心跳任務(wù)有兩個(gè)作用:
匯報(bào)自己的offset給master,這在數(shù)據(jù)傳播起到了關(guān)鍵性作用,因?yàn)閙aster節(jié)點(diǎn)向slave節(jié)點(diǎn)傳播數(shù)據(jù),offset是一項(xiàng)非常重要的指標(biāo)。
判斷master是否在線
在心跳階段應(yīng)該注意:當(dāng)slave節(jié)點(diǎn)多數(shù)掉線,或者延遲過高時(shí),master節(jié)點(diǎn)為了保證數(shù)據(jù)的穩(wěn)定性,將拒絕所有信息的同步。有如下配置:
min-slaves-to-write?2 min-slaves-max-lag?8
上述配置含義是:當(dāng)slave數(shù)量小于2個(gè),或者所有的slave的延遲都大于等于8秒時(shí),強(qiáng)制關(guān)閉master寫功能,停止數(shù)據(jù)同步。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud
視頻教程:https://doc.iocoder.cn/video/
3.主從復(fù)制常見問題
上邊介紹的主從復(fù)制是建立在主從節(jié)點(diǎn)間的網(wǎng)絡(luò)和服務(wù)都正常的情況下,業(yè)務(wù)場(chǎng)景中要考慮更多的實(shí)際情況。
3.1 master重啟
伴隨著系統(tǒng)的運(yùn)行,master節(jié)點(diǎn)的內(nèi)存數(shù)據(jù)量變得很大的情況下,一旦master節(jié)點(diǎn)重啟,runid將發(fā)生變化,會(huì)導(dǎo)致slave的全量復(fù)制操作。
這里有一個(gè)優(yōu)化方案:在master節(jié)點(diǎn)內(nèi)部創(chuàng)建master_replid變量,使用runid相同的策略生成,長(zhǎng)度41位,發(fā)送給所有的slave節(jié)點(diǎn)。在master節(jié)點(diǎn)關(guān)閉時(shí),執(zhí)行命令shutdown save,進(jìn)行RDB的數(shù)據(jù)持久化,將runid與offset保存在RDB文件中。在RDB文件中有了repl-id和repl-offset信息以后,通過指令redis-check-rdb命令可以查看這些信息。在master節(jié)點(diǎn)重啟后,將RDB文件加載到內(nèi)存中以后,也會(huì)將repl-id和repl-offset加載到內(nèi)存中。通過info 指令可以查看:
master_repl_id?=?repl
master_repl_offset?=?repl-offset
作用是:master節(jié)點(diǎn)重啟之后會(huì)保存原來(lái)的runid,重啟后恢復(fù)該值,會(huì)讓所有的slave節(jié)點(diǎn)認(rèn)為還是之前的master節(jié)點(diǎn)。
3.2 復(fù)制積壓緩沖區(qū)太小
當(dāng)復(fù)制積壓緩沖區(qū)太小的時(shí)候,當(dāng)master節(jié)點(diǎn)寫并發(fā)很大,master節(jié)點(diǎn)和slave節(jié)點(diǎn)網(wǎng)絡(luò)有抖動(dòng)的時(shí)候,就會(huì)導(dǎo)致數(shù)據(jù)同步不及時(shí),造成offset溢出,進(jìn)而導(dǎo)致全量復(fù)制。這個(gè)時(shí)候,我們可以考慮修改復(fù)制積壓緩沖區(qū)的大小,由配置repl-backlog-size控制。設(shè)置多大比較合適呢,這要根據(jù)master的并發(fā)量和網(wǎng)絡(luò)情況做具體的評(píng)估。
3.3 slave執(zhí)行了keys * 、hgetall等命令
前邊內(nèi)容我們提到slave節(jié)點(diǎn)每秒都會(huì)發(fā)送REPLCONF ACK指令到master節(jié)點(diǎn),master節(jié)點(diǎn)調(diào)用復(fù)制函數(shù)relicationCron()同步數(shù)據(jù)給slave節(jié)點(diǎn)時(shí),如果slave節(jié)點(diǎn)執(zhí)行了keys *、hgetall等阻塞命令的時(shí)候,就會(huì)在很長(zhǎng)一段時(shí)候得不到響應(yīng)。這就會(huì)導(dǎo)致master的各種資源(輸出緩沖區(qū)、帶寬、連接)等被占用。master節(jié)點(diǎn)的CPU就會(huì)變高,slave頻繁的斷開連接。
解決方案是master節(jié)點(diǎn)通過配置:repl-timeout設(shè)置合理的超時(shí)時(shí)間(默認(rèn)60s),超過改值,master節(jié)點(diǎn)將釋放slave節(jié)點(diǎn)。
3.4 master節(jié)點(diǎn)發(fā)送ping指令頻度低,網(wǎng)絡(luò)存在丟包
master節(jié)點(diǎn)默認(rèn)10s向slave節(jié)點(diǎn)發(fā)送一次ping指令,因?yàn)閙aster節(jié)點(diǎn)不僅要處理大量的寫任務(wù),還可能維護(hù)著多個(gè)master,所以ping設(shè)置的不太及時(shí)。但是當(dāng)ping指令在網(wǎng)絡(luò)中存在丟包時(shí),master節(jié)點(diǎn)如果設(shè)置的超時(shí)時(shí)間太短,就會(huì)導(dǎo)致master節(jié)點(diǎn)與slave節(jié)點(diǎn)斷開連接。
解決方案有:提高master節(jié)點(diǎn)ping的頻度,超時(shí)時(shí)間repl-time設(shè)置為ping指令時(shí)間的5~10倍。
3.5 網(wǎng)絡(luò)信息不同步,數(shù)據(jù)發(fā)送有延遲
當(dāng)主從同步中網(wǎng)絡(luò)數(shù)據(jù)發(fā)送有延遲的時(shí)候,就會(huì)造成多個(gè)slave獲取到的數(shù)據(jù)不同步,解決方案是優(yōu)化master節(jié)點(diǎn)和slave節(jié)點(diǎn)的網(wǎng)絡(luò)環(huán)境,通常是放置在一個(gè)機(jī)房部署。另外要監(jiān)控master和slave節(jié)點(diǎn)的延遲,如果延遲過大,可以暫時(shí)屏蔽對(duì)slave節(jié)點(diǎn)的訪問。通過下面指令設(shè)置:
slave-serve-stale-data?yes?|?no
開啟后,slave節(jié)點(diǎn)僅僅能響應(yīng)info、slaveof等少數(shù)命令,除非對(duì)數(shù)據(jù)一致性要求很高,否則不要輕易這樣使用。
4.總結(jié)
本文主要總結(jié)了redis實(shí)現(xiàn)主從復(fù)制的實(shí)現(xiàn)細(xì)節(jié)和注意事項(xiàng)。redis的主從復(fù)制是實(shí)現(xiàn)高可用的重要基石,后邊的文章將總結(jié)哨兵和集群的搭建。
編輯:黃飛
?
評(píng)論
查看更多