使用Redis怎么實(shí)現(xiàn)高可用和持久化?相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
創(chuàng)新互聯(lián)專注于濱海新區(qū)企業(yè)網(wǎng)站建設(shè),自適應(yīng)網(wǎng)站建設(shè),商城系統(tǒng)網(wǎng)站開發(fā)。濱海新區(qū)網(wǎng)站建設(shè)公司,為濱海新區(qū)等地區(qū)提供建站服務(wù)。全流程專業(yè)公司,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)雖然單臺(tái)Redis的的性能很好,但是Redis的單節(jié)點(diǎn)并不能保證它不會(huì)掛了啊,畢竟單節(jié)點(diǎn)的Redis是有上限的,而且人家單節(jié)點(diǎn)又要讀又要寫,小身板扛不住咋辦,所以為了保證高可用,一般都是做成集群。
主從(Master-Slave)
Redis官方是支持主從同步的,而且還支持從從同步,從從同步也可以理解為主從同步,只不過從從同步的主節(jié)點(diǎn)是另一個(gè)主從的從節(jié)點(diǎn)。
有了主從同步的集群,那么主節(jié)點(diǎn)就負(fù)責(zé)提供寫操作,而從節(jié)點(diǎn)就負(fù)責(zé)支持讀操作。
那么他們之間是如何進(jìn)行數(shù)據(jù)同步的呢?
如果Slave(從節(jié)點(diǎn))是第一次跟Master進(jìn)行連接,
那么會(huì)首先會(huì)向Master發(fā)送同步請(qǐng)求psync
;
主節(jié)點(diǎn)接收到同步請(qǐng)求,開始fork主子進(jìn)程開始進(jìn)行全量同步,然后生成RDB文件;
這個(gè)時(shí)候主節(jié)點(diǎn)同時(shí)會(huì)將新的寫請(qǐng)求,保存到緩存區(qū)(buffer)中;
從節(jié)點(diǎn)接收到RDB文件后,先清空老數(shù)據(jù),然后將RDB中數(shù)據(jù)加載到內(nèi)存中;
等到從節(jié)點(diǎn)將RDB文件同步完成后再同步緩存區(qū)中的寫請(qǐng)求。
數(shù)組被占滿之后就會(huì)覆蓋掉最早之前的數(shù)據(jù)。
所以如果由于網(wǎng)絡(luò)或是其他原因,造成緩存區(qū)中的數(shù)據(jù)被覆蓋了,那么當(dāng)從節(jié)點(diǎn)處理完主節(jié)點(diǎn)的RDB文件后,就不得不又要進(jìn)行一全量的RDB文件的復(fù)制,才能保證主從節(jié)點(diǎn)的數(shù)據(jù)一致。
如果不設(shè)置好合理的buffer區(qū)空間,是會(huì)造成一個(gè)RDB復(fù)制的死循環(huán)。
當(dāng)主從間的數(shù)據(jù)同步完成之后,后面主節(jié)點(diǎn)的每次寫操作就都會(huì)同步到從節(jié)點(diǎn),這樣進(jìn)行增量同步了。
由于負(fù)載的不斷上升就導(dǎo)致了主從之間的延時(shí)變大,所以就有了上面我說的從從同步了,主節(jié)點(diǎn)先同步到一部分從節(jié)點(diǎn),然后由從節(jié)點(diǎn)去同步其他的從節(jié)點(diǎn)。
在Redis從2.8.18開始支持無盤復(fù)制,主節(jié)點(diǎn)通過套接字,一邊遍歷內(nèi)存中的數(shù)據(jù),一邊讓數(shù)據(jù)發(fā)送給從節(jié)點(diǎn),從節(jié)點(diǎn)和之前一樣,先將數(shù)據(jù)存儲(chǔ)在磁盤文件中,然后再一次性加載。
另外由于主從同步是異步的,所以從Redis3.0之后出現(xiàn)了同步復(fù)制,就是通過wait命令來進(jìn)行控制,wait命令有兩個(gè)參數(shù),第一個(gè)是從庫數(shù)量,第二個(gè)是等待從庫的復(fù)制時(shí)間,如果第二個(gè)參數(shù)設(shè)置為0,那么就是代表要等待所有從庫都復(fù)制完才去執(zhí)行后面的命令。
但是這樣就會(huì)存在一個(gè)隱患,當(dāng)網(wǎng)絡(luò)異常后,wait命令會(huì)一直阻塞下去,導(dǎo)致Redis不可用。
哨兵可以監(jiān)控Redis集群的健康狀態(tài),當(dāng)主節(jié)點(diǎn)掛掉之后,選舉出新的主節(jié)點(diǎn)??蛻舳嗽谑褂肦edis的時(shí)候會(huì)先通過Sentinel來獲取主節(jié)點(diǎn)地址,然后再通過主節(jié)點(diǎn)來進(jìn)行數(shù)據(jù)交互。當(dāng)主節(jié)點(diǎn)掛掉之后,客戶端會(huì)再次向Sentinel獲取主節(jié)點(diǎn),這樣客戶端就可以無感知的繼續(xù)使用了。
哨兵集群工作過程,主節(jié)點(diǎn)掛掉之后會(huì)選舉出新的主節(jié)點(diǎn),然后監(jiān)控掛掉的節(jié)點(diǎn),當(dāng)掛掉的節(jié)點(diǎn)恢復(fù)后,原先的主節(jié)點(diǎn)就會(huì)變成從節(jié)點(diǎn),從新的主節(jié)點(diǎn)那里建立主從關(guān)系。
Redis Cluster是Redis官方推薦的集群模式,Redis Cluster將所有數(shù)據(jù)劃分到16384個(gè)槽(slots
)中,每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分槽位的讀寫操作。
Redis Cluster默認(rèn)是通過CRC16算法獲取到key的hash值,然后再對(duì)16384進(jìn)行取余(CRC16(key)%16384
),獲取到的槽位在哪個(gè)節(jié)點(diǎn)負(fù)責(zé)的范圍內(nèi)(這里一般是會(huì)有一個(gè)槽位和節(jié)點(diǎn)的映射表來進(jìn)行快速定位節(jié)點(diǎn)的,通常使用bitmap來實(shí)現(xiàn)),就存儲(chǔ)在哪個(gè)節(jié)點(diǎn)上。
當(dāng)Redis Cluster的客戶端在和集群建立連接的時(shí)候,也會(huì)獲得一份槽位和節(jié)點(diǎn)的配置關(guān)系(槽位和節(jié)點(diǎn)的映射表),這樣當(dāng)客戶端要查找某個(gè)key時(shí),可以直接定位到目標(biāo)節(jié)點(diǎn)。
但是當(dāng)客戶端發(fā)送請(qǐng)求時(shí),如果接收請(qǐng)求的節(jié)點(diǎn)發(fā)現(xiàn)該數(shù)據(jù)的槽位并不在當(dāng)前節(jié)點(diǎn)上,那么會(huì)返回MOVED
指令將正確的槽位和節(jié)點(diǎn)信息返回給客戶端,客戶接著請(qǐng)求正確的節(jié)點(diǎn)獲取數(shù)據(jù)。
一般客戶端在接收到MOVED
指令后,也會(huì)更新自己本地的槽位和節(jié)點(diǎn)的映射表,這樣下次獲取數(shù)據(jù)時(shí)就可以直接命中了。這整個(gè)重定向的過程對(duì)客戶端是透明的。
當(dāng)集群中新增節(jié)點(diǎn)或刪除節(jié)點(diǎn)后,節(jié)點(diǎn)間的數(shù)據(jù)遷移是按槽位為單位的,一個(gè)槽位一個(gè)槽位的遷移,當(dāng)遷移時(shí)原節(jié)點(diǎn)狀態(tài)處于:magrating
,目標(biāo)節(jié)點(diǎn)處于:importing
。
在遷移過程中,客戶端首先訪問舊節(jié)點(diǎn),如果數(shù)據(jù)還在舊節(jié)點(diǎn),那么舊節(jié)點(diǎn)正常處理,如果不在舊節(jié)點(diǎn),就會(huì)返回一個(gè)-ASK + 目標(biāo)節(jié)點(diǎn)地址
的指令,客戶端收到這個(gè)-ASK
指令后,向目標(biāo)節(jié)點(diǎn)執(zhí)行一個(gè)asking
指令(告訴新節(jié)點(diǎn),必須處理客戶端這個(gè)數(shù)據(jù)),然后再向目標(biāo)節(jié)點(diǎn)執(zhí)行客戶端的訪問數(shù)據(jù)的指令。
Redis Cluster可以為每個(gè)主節(jié)點(diǎn)設(shè)置多個(gè)從節(jié)點(diǎn),當(dāng)單個(gè)主節(jié)點(diǎn)掛掉后,集群會(huì)自動(dòng)將其中某個(gè)從節(jié)點(diǎn)提升為主節(jié)點(diǎn),若沒有從節(jié)點(diǎn),那么集群將處于不可用狀態(tài)。
Redis提供了一個(gè)參數(shù):cluster-require-full-coverage
,用來配置可以允許部分節(jié)點(diǎn)出問題后,還有其他節(jié)點(diǎn)在運(yùn)行時(shí)可以正常提供服務(wù)。
另外一點(diǎn)比較特殊的是,Cluster中當(dāng)一個(gè)節(jié)點(diǎn)發(fā)現(xiàn)某個(gè)其他節(jié)點(diǎn)出現(xiàn)失聯(lián)了,這個(gè)時(shí)候問題節(jié)點(diǎn)只是PFail
(Possibly
-可能下線),然后它會(huì)把這個(gè)失聯(lián)信息廣播給其他節(jié)點(diǎn),當(dāng)一個(gè)節(jié)點(diǎn)接收到某個(gè)節(jié)點(diǎn)的失聯(lián)信息達(dá)到集群的大多數(shù)時(shí),就可以將失聯(lián)節(jié)點(diǎn)標(biāo)記為下線,然后將下線信息廣播給其他節(jié)點(diǎn)。若失聯(lián)節(jié)點(diǎn)為主節(jié)點(diǎn),那么將立即對(duì)該節(jié)點(diǎn)進(jìn)行主從切換。
Redis高可用就先說到這里吧,后面其實(shí)還有Codis,但是目前Cluster逐漸流行起來了,Codis的競(jìng)爭(zhēng)力逐漸被蠶食,而且對(duì)新版本的支持,更新的也比較慢,所以這里就不說它了,感興趣的可以自己去了解一下,國(guó)人開源的Redis集群模式Codis。
Redis持久化的意義在于,當(dāng)出現(xiàn)宕機(jī)問題后,能將數(shù)據(jù)恢復(fù)到緩存中,它提供了兩種持久化機(jī)制:一種是快照(RDB),一種是AOF日志。
快照是一次全量備份,而AOF是增量備份??煺帐莾?nèi)存數(shù)據(jù)的二進(jìn)制序列化形式,存儲(chǔ)上非常緊湊,而AOF日志記錄的是內(nèi)存數(shù)據(jù)修改的指令記錄文本。
因?yàn)镽edis是單線程的,所以在做快照持久化的時(shí)候,通常有兩個(gè)選擇,save命令,會(huì)阻塞線程,直到備份完成;bgsave會(huì)異步的執(zhí)行備份,其實(shí)是fork出了一個(gè)子進(jìn)程,用子進(jìn)程去執(zhí)行快照持久化操作,將數(shù)據(jù)保存在一個(gè).rdb文件中。
子進(jìn)程剛剛產(chǎn)生的時(shí)候,是和父進(jìn)程共享內(nèi)存中的數(shù)據(jù)的,但是子進(jìn)程做持久化時(shí),是不會(huì)修改數(shù)據(jù)的,而父進(jìn)程是要持續(xù)提供服務(wù)的,所以父進(jìn)程就會(huì)持續(xù)的修改內(nèi)存中的數(shù)據(jù),這個(gè)時(shí)候父進(jìn)程就會(huì)將內(nèi)存中的數(shù)據(jù),Copy出一份來進(jìn)行修改。
父進(jìn)程copy的數(shù)據(jù)是以數(shù)據(jù)頁為單位的(4k一頁),對(duì)那一頁數(shù)據(jù)進(jìn)行修改就copy哪一頁的數(shù)據(jù)。
子進(jìn)程由于數(shù)據(jù)沒有變化就會(huì)一直的去遍歷數(shù)據(jù),進(jìn)程持久化操作了,這就是只保留了創(chuàng)建子進(jìn)程的時(shí)候的快照。
那么RDB是在什么時(shí)候觸發(fā)的呢?
# savesave 60 10000 save 300 10
上這段配置就是在redis.conf文件中配置的,第一個(gè)參數(shù)是時(shí)間單位是秒,第二個(gè)參數(shù)執(zhí)行數(shù)據(jù)變化的次數(shù)。
意思就是說:300秒之內(nèi)至少發(fā)生10次寫操作、
60秒之內(nèi)發(fā)生至少10000次寫操作,只要滿足任一條件,均會(huì)觸發(fā)bgsave
Redis在接收到客戶端請(qǐng)求指令后,會(huì)先進(jìn)行校驗(yàn),校驗(yàn)成功后,立即將指令存儲(chǔ)到AOF日志文件中,就是說,Redis是先記錄日志,再執(zhí)行命令。這樣即使命令還沒執(zhí)行突然宕機(jī)了,通過AOF日志文件也是可以恢復(fù)的。
AOF日志文件,隨著時(shí)間的推移,會(huì)越來越大,所以就需要進(jìn)行重寫瘦身。AOF重寫的原理就是,fork一個(gè)子進(jìn)程,對(duì)內(nèi)存進(jìn)行遍歷,然后生成一系列的Redis指令,然后序列化到一個(gè)新的aof文件中。然后再將遍歷內(nèi)存階段的增量日志,追加到新的aof文件中,追加完成后立即替換舊的aof文件,這樣就完成了AOF的瘦身重寫。
因?yàn)锳OF是一個(gè)寫文件的IO操作,是比較耗時(shí)。所以AOF日志并不是直接寫入到日志文件的,而是先寫到一個(gè)內(nèi)核的緩存中,然后通過異步刷臟,來將數(shù)據(jù)保存到磁盤的。
由于這個(gè)情況,就導(dǎo)致了會(huì)有還沒來得急刷臟然后就宕機(jī)了,導(dǎo)致數(shù)據(jù)丟失的風(fēng)險(xiǎn)。
所以Redis提供了一個(gè)配置,可以手動(dòng)的來選擇刷臟的頻率。
always:每一條AOF記錄都立即同步到文件,性能很低,但較為安全。
everysec:每秒同步一次,性能和安全都比較中庸的方式,也是redis推薦的方式。如果遇到物理服務(wù)器故障,可能導(dǎo)致最多1秒的AOF記錄丟失。
no:Redis永不直接調(diào)用文件同步,而是讓操作系統(tǒng)來決定何時(shí)同步磁盤。性能較好,但很不安全。
AOF默認(rèn)是關(guān)閉的,需要在配置文件中手動(dòng)開啟。
# 只有在“yes”下,aof重寫/文件同步等特性才會(huì)生效 appendonly yes ## 指定aof文件名稱 appendfilename appendonly.aof ## 指定aof操作中文件同步策略,有三個(gè)合法值:always everysec no,默認(rèn)為everysec appendfsync everysec ## 在aof-rewrite期間,appendfsync是否暫緩文件同步,"no"表示“不暫緩”,“yes”表示“暫緩”,默認(rèn)為“no” no-appendfsync-on-rewrite no ## aof文件rewrite觸發(fā)的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才會(huì)觸發(fā)rewrite,默認(rèn)“64mb”,建議“512mb” auto-aof-rewrite-min-size 64mb
Redis4.0提供了一種新的持久化機(jī)制,就是RDB和AOF結(jié)合使用,將rdb文件內(nèi)容和aof文件存在一起,AOF中保存的不再是全部數(shù)據(jù)了,而是從RDB開始的到結(jié)束的增量日志。
這樣在Redis恢復(fù)數(shù)據(jù)的時(shí)候,可以先假裝RDB文件中的內(nèi)容,然后在順序執(zhí)行AOF日志中指令,這樣就將Redis重啟時(shí)恢復(fù)數(shù)據(jù)的效率得到了大幅度提升。
看完上述內(nèi)容,你們掌握使用Redis怎么實(shí)現(xiàn)高可用和持久化的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!