小編今天帶大家了解如何分析redis中的高可用方案,文中知識(shí)點(diǎn)介紹的非常詳細(xì)。覺(jué)得有幫助的朋友可以跟著小編一起瀏覽文章的內(nèi)容,希望能夠幫助更多想解決這個(gè)問(wèn)題的朋友找到問(wèn)題的答案,下面跟著小編一起深入學(xué)習(xí)“如何分析redis中的高可用方案”的知識(shí)吧。
建網(wǎng)站原本是網(wǎng)站策劃師、網(wǎng)絡(luò)程序員、網(wǎng)頁(yè)設(shè)計(jì)師等,應(yīng)用各種網(wǎng)絡(luò)程序開(kāi)發(fā)技術(shù)和網(wǎng)頁(yè)設(shè)計(jì)技術(shù)配合操作的協(xié)同工作。成都創(chuàng)新互聯(lián)專業(yè)提供做網(wǎng)站、網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站制作(企業(yè)站、響應(yīng)式網(wǎng)站設(shè)計(jì)、電商門戶網(wǎng)站)等服務(wù),從網(wǎng)站深度策劃、搜索引擎友好度優(yōu)化到用戶體驗(yàn)的提升,我們力求做到極致!
用戶可以通過(guò)SLAVEOF命令或者配置,讓一個(gè)服務(wù)器去復(fù)制另一個(gè)服務(wù)器。被復(fù)制的服務(wù)器稱為主服務(wù)器,進(jìn)行復(fù)制的服務(wù)器稱為從服務(wù)器。這樣你在主服務(wù)器上增加鍵值,同時(shí)可以在從服務(wù)器上讀取?!鞠嚓P(guān)推薦:Redis視頻教程】
復(fù)制的過(guò)程又分為同步和命令傳播兩個(gè)步驟。
同步
同步將從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)更新到主服務(wù)器當(dāng)前的數(shù)據(jù)庫(kù)狀態(tài)。
客戶端向從服務(wù)器發(fā)送SLAVEOF命令時(shí),從服務(wù)器會(huì)向主服務(wù)器發(fā)生SYNC命令
進(jìn)行同步,步驟如下:
從服務(wù)器向主服務(wù)器發(fā)生SYNC命令。
收到SYNC命令的主服務(wù)器執(zhí)行BGSAVE命令,在后臺(tái)生成一個(gè)RDB文件,并用一個(gè)緩沖區(qū)記錄從現(xiàn)在開(kāi)始執(zhí)行的所有寫命令。
主服務(wù)器的BGSAVE命令執(zhí)行完畢后,主服務(wù)器將BGSAVE生成的RDB文件發(fā)送給從服務(wù)器,從服務(wù)器接收并載入這個(gè)RDB文件,將從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)更新到主服務(wù)器執(zhí)行BGSAVE命令時(shí)的數(shù)據(jù)庫(kù)狀態(tài)
主服務(wù)器將緩沖區(qū)的所有寫命令發(fā)送給從服務(wù)器,從服務(wù)器執(zhí)行這些寫命令,將數(shù)據(jù)庫(kù)狀態(tài)更新至主服務(wù)器當(dāng)前數(shù)據(jù)庫(kù)狀態(tài)。
命令傳播
同步操作完成之后,主服務(wù)器和從服務(wù)器的數(shù)據(jù)庫(kù)狀態(tài)是一致的,但主服務(wù)器又接收到客戶端寫命令后,主從數(shù)據(jù)庫(kù)之間又產(chǎn)生了數(shù)據(jù)不一致,這時(shí)通過(guò)命令傳播達(dá)到數(shù)據(jù)庫(kù)一致。
PSYNC同步的優(yōu)化
2.8之前的同步每次都是全量同步,而如果是從服務(wù)器只是斷開(kāi)連接了一會(huì),事實(shí)上是不用從頭開(kāi)始同步的,只需要將斷開(kāi)連接這會(huì)的數(shù)據(jù)同步即可。所以2.8版本開(kāi)始使用PSYNC來(lái)代替SYNC命令。
PSYNC分成全量同步和部分同步兩種情況,全量同步就是處理初次同步的狀態(tài),而部分同步就是處理斷線重連這種情況。
部分同步的實(shí)現(xiàn)
部分同步主要使用了以下三部分:
主服務(wù)器的復(fù)制偏移量和從服務(wù)器的復(fù)制偏移量
主服務(wù)器的復(fù)制積壓緩沖區(qū)
服務(wù)器的運(yùn)行ID
復(fù)制偏移量
主服務(wù)器的復(fù)制偏移量:主服務(wù)器每次向從服務(wù)器傳播N個(gè)字節(jié)的數(shù)據(jù)時(shí),就將自己的復(fù)制偏移量+N從服務(wù)器的復(fù)制偏移量:從服務(wù)器每次收到主服務(wù)器傳播的N個(gè)字節(jié)數(shù)據(jù),就將自己的復(fù)制偏移量+N 如果主從服務(wù)器處于一致?tīng)顟B(tài),那么它們的偏移量總是相同的,如果偏移量不相等,那么說(shuō)明它們處于不一致?tīng)顟B(tài)。
復(fù)制積壓緩沖區(qū)
復(fù)制積壓緩沖區(qū)由主服務(wù)器維護(hù)的一個(gè)固定長(zhǎng)度的FIFO隊(duì)列,默認(rèn)大小1MB,達(dá)到最大長(zhǎng)度后,最先入隊(duì)的會(huì)被彈出,給新入隊(duì)的元素讓位置。
redis命令傳播的時(shí)候不但會(huì)發(fā)送給從服務(wù)器,還會(huì)發(fā)送給復(fù)制積壓緩沖區(qū)。
當(dāng)從服務(wù)器重連上主服務(wù)器時(shí),從服務(wù)器會(huì)通過(guò)PSYNC命令將自己的復(fù)制偏移量offset發(fā)送給主服務(wù)器,主服務(wù)器根據(jù)復(fù)制偏移量來(lái)決定使用部分同步還是全量同步。
如果offset偏移量之后的數(shù)據(jù)還在復(fù)制積壓緩沖區(qū),那么使用部分同步,反之使用全量同步。
(書(shū)上沒(méi)說(shuō)是怎么判斷的,我猜測(cè)應(yīng)該是拿主復(fù)制偏移量減去從復(fù)制偏移量,如果大于1MB就說(shuō)明有數(shù)據(jù)不在緩沖積壓區(qū)?)
服務(wù)器的運(yùn)行ID
服務(wù)器啟動(dòng)時(shí)會(huì)生成一個(gè)40位隨機(jī)的字符作為服務(wù)器運(yùn)行ID。
從服務(wù)器對(duì)主服務(wù)器初次復(fù)制時(shí),主服務(wù)器會(huì)將自己的運(yùn)行ID傳送給從服務(wù)器,而從服務(wù)器會(huì)將這個(gè)運(yùn)行ID保存下來(lái)。從服務(wù)器斷線重連的時(shí)候,會(huì)將保存的運(yùn)行ID發(fā)送過(guò)去,如果從服務(wù)器保存的運(yùn)行ID和當(dāng)前主服務(wù)器的運(yùn)行ID相同,那么會(huì)嘗試部分同步,如果不同會(huì)執(zhí)行全量同步。
PSYNC的整體流程
心跳檢測(cè)
在命令傳播階段,從服務(wù)器會(huì)默認(rèn)以每秒一次的頻率,向主服務(wù)器發(fā)送命令:REPLICONF ACK
其中replication_offset就是從服務(wù)器當(dāng)前的復(fù)制偏移量。
發(fā)送REPLICONF ACK命令對(duì)于主從服務(wù)器有三個(gè)作用:
檢測(cè)主從服務(wù)器的網(wǎng)絡(luò)連接狀態(tài)。
輔助實(shí)現(xiàn)min-slaves選項(xiàng)。
檢測(cè)命令丟失。
檢測(cè)主從服務(wù)器的網(wǎng)絡(luò)連接狀態(tài)
主從服務(wù)器可以通過(guò)發(fā)送和接收REPLICONF ACK命令來(lái)檢查兩者之間的網(wǎng)絡(luò)連接是否正常:如果主服務(wù)器超過(guò)一秒鐘沒(méi)有收到從服務(wù)器發(fā)來(lái)的REPLICONF ACK命令,那么主服務(wù)器就知道主從之間出現(xiàn)問(wèn)題了。
輔助實(shí)現(xiàn)min-slaves選項(xiàng)
redis的min-slaves-to-write
和min-slaves-max-lag
兩個(gè)選項(xiàng)可以防止主從服務(wù)器在不安全的情況下執(zhí)行寫命令。
min-slaves-to-write 3 min-slaves-max-lag 10
如果配置如上,就表示如果從服務(wù)器的數(shù)量少于3個(gè),或者3個(gè)從服務(wù)器的延遲都大于或等于10秒時(shí),那么主服務(wù)器就將拒絕執(zhí)行寫命令。
檢測(cè)命令丟失
如果因?yàn)榫W(wǎng)絡(luò)故障,主服務(wù)器傳播給從服務(wù)器的寫命令在半路丟失,那么從服務(wù)器向主服務(wù)器發(fā)送REPLICONF ACK命令時(shí),主服務(wù)器將發(fā)覺(jué)從服務(wù)器當(dāng)前的復(fù)制偏移量少于自己的偏移量,那么主服務(wù)器可以根據(jù)從服務(wù)器的復(fù)制偏移量,在復(fù)制緩沖區(qū)當(dāng)中找到從服務(wù)器缺少的數(shù)據(jù),將這些數(shù)據(jù)重寫發(fā)送給從服務(wù)器。
主從復(fù)制總結(jié)
其實(shí)主從復(fù)制就是多備份了一份數(shù)據(jù),因?yàn)榧词褂蠷DB和AOF進(jìn)行持久化,但是可能主服務(wù)器上整個(gè)機(jī)器掛掉了,而主從復(fù)制可以將主從服務(wù)器部署在兩臺(tái)不同的機(jī)器上,這樣即使主服務(wù)器的機(jī)器掛掉了,也可以手動(dòng)切換到從服務(wù)器繼續(xù)服務(wù)。
主從雖然實(shí)現(xiàn)了數(shù)據(jù)的備份,但當(dāng)主服務(wù)器掛掉時(shí),需要手動(dòng)的將從服務(wù)器切換成主服務(wù)器。而sentinel就可以實(shí)現(xiàn)當(dāng)主服務(wù)器掛掉時(shí),自動(dòng)將從服務(wù)器切換成主服務(wù)器。
sentinel系統(tǒng)可以監(jiān)視所有的主從服務(wù)器,假設(shè)server1現(xiàn)在下線。當(dāng)server1的下線時(shí)長(zhǎng)超過(guò)用戶設(shè)定的下線時(shí)長(zhǎng)上限時(shí),sentinel系統(tǒng)就會(huì)對(duì)server1執(zhí)行故障轉(zhuǎn)移:
首先sentinel系統(tǒng)會(huì)挑選server1下的其中一個(gè)從服務(wù)器,并將這個(gè)選中的從服務(wù)器升級(jí)成新的主服務(wù)器。
之后,sentinel系統(tǒng)會(huì)向server1屬下的所有從服務(wù)器發(fā)送新的復(fù)制命令,讓他們成為新主服務(wù)器的從服務(wù)器。當(dāng)所有從服務(wù)器復(fù)制新的主服務(wù)器時(shí),故障轉(zhuǎn)移操作執(zhí)行完畢。
另外,sentinel還會(huì)監(jiān)視已下線的server1,在它重新上線時(shí),將它設(shè)置為新的主服務(wù)器的從服務(wù)器。
初始化sentinel狀態(tài)
struct sentinelState { char myid[CONFIG_RUN_ID_SIZE+1]; // 當(dāng)前紀(jì)元,用于實(shí)現(xiàn)故障轉(zhuǎn)移 uint64_t current_epoch; // 保存了所有被這個(gè)sentinel監(jiān)視的主服務(wù)器 // 字典的鍵是主服務(wù)器的名字 // 字典的值是指向sentinelRedisInstance結(jié)構(gòu)的指針 dict *masters; // 是否進(jìn)入了TILT模式 int tilt; // 目前正在執(zhí)行的腳本數(shù)量 int running_scripts; // 進(jìn)入TILT模式的時(shí)間 mstime_t tilt_start_time; // 最后一次執(zhí)行時(shí)間處理器的時(shí)間 mstime_t previous_time; // 一個(gè)fifo隊(duì)列,包含了所有需要執(zhí)行的用戶腳本 list *scripts_queue; char *announce_ip; int announce_port; unsigned long simfailure_flags; int deny_scripts_reconfig; char *sentinel_auth_pass; char *sentinel_auth_user; int resolve_hostnames; int announce_hostnames; } sentinel;
初始化sentinel狀態(tài)的masters屬性
masters記錄了所有被sentinel監(jiān)視的主服務(wù)器的相關(guān)信息,其中字典的鍵是被監(jiān)視服務(wù)器的名字,而值是被監(jiān)視服務(wù)器對(duì)應(yīng)著sentinelRedisInstance結(jié)構(gòu)。sentinelRedisInstance被sentinel服務(wù)器監(jiān)視的實(shí)例,可以是主服務(wù)器、從服務(wù)器或其他sentinel實(shí)例。
typedef struct sentinelRedisInstance { // 標(biāo)識(shí)值,記錄實(shí)例的類型,以及該實(shí)例的當(dāng)前狀態(tài) int flags; // 實(shí)例的名字 // 主服務(wù)器名字在配置文件中設(shè)置 // 從服務(wù)器和sentinel名字由sentinel自動(dòng)設(shè)置,格式是ip:port char *name; // 運(yùn)行id char *runid; // 配置紀(jì)元,用于實(shí)現(xiàn)故障轉(zhuǎn)移 uint64_t config_epoch; // 實(shí)例的地址 sentinelAddr *addr; /* Master host. */ // 實(shí)例無(wú)響應(yīng)多少毫秒之后,判斷為主觀下線 mstime_t down_after_period; // 判斷這個(gè)實(shí)例為客觀下線所需的支持投票數(shù)量 unsigned int quorum; // 執(zhí)行故障轉(zhuǎn)移,可以同時(shí)對(duì)新的主服務(wù)器進(jìn)行同步的從服務(wù)器數(shù)量 int parallel_syncs; // 刷新故障遷移狀態(tài)的最大時(shí)限 mstime_t failover_timeout; // 除了自己外,其他監(jiān)視主服務(wù)器的sentinel // 鍵是sentinel的名字,格式是ip:port // 值是鍵對(duì)應(yīng)的sentinel的實(shí)例結(jié)構(gòu) dict *sentinels; // ... } sentinelRedisInstance;
創(chuàng)建連向主服務(wù)器的網(wǎng)絡(luò)連接
初始化sentinel的最后一步是創(chuàng)建連向被監(jiān)視主服務(wù)器的網(wǎng)絡(luò)連接,會(huì)創(chuàng)建兩個(gè)連向主服務(wù)器的連接。
命令連接:專門向主服務(wù)器發(fā)送命令,并接收命令回復(fù)。
訂閱連接:專門用于訂閱主服務(wù)器的_sentinel_:hello頻道。
獲取主服務(wù)器信息
sentinel默認(rèn)會(huì)每10秒,通過(guò)命令連接向被監(jiān)視的主服務(wù)器發(fā)送INFO命令,并通過(guò)回復(fù)獲取主服務(wù)器當(dāng)前的信息?;貜?fù)可以獲得以下信息。
主服務(wù)器的run_id
主服務(wù)器下所有從服務(wù)器的信息。
根據(jù)這些信息可以更新sentinelRedisInstance下的name字典和runid字段。
獲取從服務(wù)器信息
sentinel也會(huì)創(chuàng)建連接到從服務(wù)器的命令連接和訂閱連接。
sentinel默認(rèn)會(huì)每10秒,通過(guò)命令連接向從服務(wù)器發(fā)送INFO命令,并通過(guò)回復(fù)獲取從服務(wù)器當(dāng)前的信息?;貜?fù)如下:
從服務(wù)器的運(yùn)行ID
從服務(wù)器的角色role
主服務(wù)器的ip和端口
主服務(wù)器的連接狀態(tài)master_link_status
從服務(wù)器的優(yōu)先級(jí)slave_priority
從服務(wù)器的復(fù)制偏移變量
根據(jù)info的回復(fù)信息,sentinel可以更新從服務(wù)器的實(shí)例結(jié)構(gòu)。
向主服務(wù)器和從服務(wù)器的訂閱連接發(fā)送信息
默認(rèn)情況下,sentinel會(huì)每2秒一次,向被監(jiān)視的主服務(wù)器和從服務(wù)器發(fā)送命令。
s_ip
:sentinel的ip地址s_port
:sentinel的端口號(hào)s_runid
:sentinel的運(yùn)行ids_epoch
:sentinel當(dāng)前的配置紀(jì)元m_name
:主服務(wù)器的名字m_ip
:主服務(wù)器的ip地址m_port
:主服務(wù)器的端口號(hào)m_epoch
:主服務(wù)器當(dāng)前的配置紀(jì)元
向sentinel_:hello頻道發(fā)送信息,也會(huì)被監(jiān)視同一個(gè)服務(wù)器的其他sentinel監(jiān)聽(tīng)到(包括自己)。
創(chuàng)建連向其他sentinel的命令連接
sentinel之間會(huì)互相創(chuàng)建命令連接。監(jiān)視同一個(gè)囑咐其的多個(gè)sentinel將形成相互連接的網(wǎng)絡(luò)。
sentinel之間不會(huì)創(chuàng)建訂閱連接。
檢測(cè)主觀下線狀態(tài)
sentinel會(huì)每秒一次向所有與它創(chuàng)建了命令連接的實(shí)例(主服務(wù)器、從服務(wù)器、其他sentinel)發(fā)送ping命令,通過(guò)實(shí)例的回復(fù)來(lái)判斷實(shí)例是否在線。
有效回復(fù):實(shí)例返回+PONG、-LOADING、-MASTERDOWN其中一種。
無(wú)效回復(fù):以上三種回復(fù)之外的其他回復(fù),或者指定時(shí)長(zhǎng)內(nèi)沒(méi)回復(fù)。
某個(gè)實(shí)例在down-after-milliseconds
毫秒內(nèi),連續(xù)向sentinel返回?zé)o效回復(fù)。那么sentinel就會(huì)修改這個(gè)實(shí)例對(duì)應(yīng)的實(shí)例結(jié)構(gòu),在結(jié)構(gòu)的flags屬性中打開(kāi)SRI_S_DOWN標(biāo)識(shí),標(biāo)識(shí)該實(shí)例進(jìn)入主觀下線狀態(tài)。(down-after-milliseconds可以在sentinel的配置文件中配置)
檢測(cè)客觀下線狀態(tài)
當(dāng)sentinel將一個(gè)主服務(wù)器判斷為主觀下線后,為了確認(rèn)這個(gè)主服務(wù)器是否真的下線,還會(huì)想其他同樣監(jiān)視這個(gè)主服務(wù)器的其他sentinel詢問(wèn),看其他sentinel是否也認(rèn)為該主服務(wù)器下線了。超過(guò)一定數(shù)量就將主服務(wù)器判斷為客觀下線。
詢問(wèn)其他sentinel是否同意該服務(wù)器下線
SENTINEL is-master-down-by-addr
通過(guò)SENTINEL is-master-down-by-addr命令詢問(wèn),參數(shù)意義如下圖:
接收SENTINEL is-master-down-by-addr命令
其他sentinel接收到SENTINEL is-master-down-by-addr命令后,會(huì)根據(jù)其中主服務(wù)器的ip和端口,檢查主服務(wù)器是否下線,然后返回包含三個(gè)參數(shù)的Multi Bulk的回復(fù)。
sentinel統(tǒng)計(jì)其他sentinel同意主服務(wù)器已下線的數(shù)量,達(dá)到配置的數(shù)量后,則將主服務(wù)器的flags屬性的SRI_O_DOWN標(biāo)識(shí)打開(kāi),表示主服務(wù)器已經(jīng)進(jìn)入客觀下線狀態(tài)。
選舉領(lǐng)頭sentinel
當(dāng)一個(gè)主服務(wù)器被判斷成客觀下線時(shí),監(jiān)視這個(gè)下線主服務(wù)器的各個(gè)sentinel就會(huì)協(xié)商選舉一個(gè)新的領(lǐng)頭sentinel,由這個(gè)sentinel進(jìn)行故障轉(zhuǎn)移操作。
確認(rèn)主服務(wù)器進(jìn)入客觀下線狀態(tài)后,會(huì)再次發(fā)送SENTINEL is-master-down-by-addr命令來(lái)選舉出領(lǐng)頭sentinel。
選舉規(guī)則
監(jiān)視同一個(gè)主服務(wù)器的多個(gè)在線sentinel中每一個(gè)都可能成為領(lǐng)頭sentinel。
每次進(jìn)行領(lǐng)頭sentinel選舉之后,無(wú)論選舉是否成功,所有sentinel的配置紀(jì)元(configuration epoch)的值都會(huì)自增一次。(配置紀(jì)元,其實(shí)就是一個(gè)計(jì)數(shù)器)
在一個(gè)配置紀(jì)元里,所有sentinel都有將某個(gè)sentinel設(shè)置成局部sentinel的機(jī)會(huì),一旦設(shè)置在這個(gè)配置紀(jì)元里就不能再更改。
所有發(fā)現(xiàn)主服務(wù)器客觀下線的sentinel都會(huì)要求其他sentinel將自己設(shè)置為局部領(lǐng)頭sentinel,也就是都會(huì)發(fā)送SENTINEL is-master-down-by-addr命令,嘗試讓其他sentinel將自己設(shè)置成局部領(lǐng)頭sentinel。
當(dāng)一個(gè)sentinel向另一個(gè)sentinel發(fā)送SENTINEL is-master-down-by-addr
命令時(shí),如果runid參數(shù)的值不是*,而是源sentinel的runid,就表示要目標(biāo)sentinel將自己設(shè)置成領(lǐng)頭sentinel。
sentinel設(shè)置局部領(lǐng)頭的規(guī)則是先到先得
,第一個(gè)設(shè)置為局部領(lǐng)頭sentinel后,其他的請(qǐng)求都被拒絕。
目標(biāo)sentinel在接收到一條SENTINEL is-master-down-by-addr命令后,將向源sentinel返回一個(gè)命令回復(fù)?;貜?fù)中l(wèi)eader_runid參數(shù)和leader_epoch參數(shù)分別記錄了目標(biāo)sentinel的局部領(lǐng)頭sentinel的runid和配置紀(jì)元。
源sentinel接收到回復(fù)之后,會(huì)比較返回的配置紀(jì)元是否和自己的配置紀(jì)元相同,如果一樣再繼續(xù)比較返回的局部領(lǐng)頭sentinel的runid是否和自己的runid相同,如果一致就表示目標(biāo)sentinel將自己設(shè)置成了局部領(lǐng)頭sentinel。
如果某個(gè)sentinel被半數(shù)以上的sentinel設(shè)置成了局部領(lǐng)頭sentinel,那么它就成為領(lǐng)頭sentinel。
領(lǐng)頭sentinel需要半數(shù)以上支持,并且每個(gè)配置紀(jì)元內(nèi)只能設(shè)置一次,那么一個(gè)配置紀(jì)元里,只會(huì)出現(xiàn)一個(gè)領(lǐng)頭sentinel
如果在一定時(shí)限內(nèi),每一個(gè)sentinel被選舉成領(lǐng)頭sentinel(沒(méi)人沒(méi)獲取半數(shù)以上選票),那么各個(gè)sentinel在一段時(shí)間之后再次選舉,直到選出領(lǐng)頭sentinel
故障轉(zhuǎn)移
故障轉(zhuǎn)移包含以下三個(gè)步驟:
在已下線的主服務(wù)器下所有從服務(wù)器里,挑選出一個(gè)從服務(wù)器轉(zhuǎn)換成主服務(wù)器。
讓已下線的主服務(wù)器屬下的所有從服務(wù)器改為復(fù)制新的主服務(wù)器。
將已經(jīng)下線的主服務(wù)器設(shè)置為新服務(wù)器的從服務(wù)器,舊的主服務(wù)器重新上線后,它就成為新的主服務(wù)器的從服務(wù)器。
選出新的主服務(wù)器
已下線的主服務(wù)器下所有從服務(wù)器里,挑選出一個(gè)從服務(wù)器,向這個(gè)從服務(wù)器發(fā)送SLAVEOF no one命令,將這個(gè)從服務(wù)器轉(zhuǎn)換成主服務(wù)器。
挑選新主服務(wù)器的規(guī)則
領(lǐng)頭的sentinel會(huì)將已下線主服務(wù)器的所有從服務(wù)器保存到一個(gè)列表里面,然后對(duì)這個(gè)列表進(jìn)行過(guò)濾,挑選出新的主服務(wù)器。
刪除列表中所有處于下線或者斷線狀態(tài)的從服務(wù)器。
刪除列表中所有最近五秒內(nèi)沒(méi)有回復(fù)過(guò)領(lǐng)頭sentinel的INFO命令的從服務(wù)器
刪除所有與已下線服務(wù)器連接斷開(kāi)超過(guò) dwon-after-milliseconds * 10毫秒的服務(wù)器
然后根據(jù)從服務(wù)器的優(yōu)先級(jí),對(duì)列表中剩余的從服務(wù)器進(jìn)行排序,并選出其中優(yōu)先級(jí)最高的服務(wù)器。
如果有多個(gè)相同最高優(yōu)先級(jí)的從服務(wù)器,那么就根據(jù)復(fù)制偏移量進(jìn)行排序,選出最大偏移量的從服務(wù)器(復(fù)制偏移量最大也代表它保存的數(shù)據(jù)最新)
如果復(fù)制偏移量也相同,那么就根據(jù)runid進(jìn)行排序,選其中runid最小的從服務(wù)器
發(fā)送slaveof no one 命令之后,領(lǐng)頭sentinel會(huì)每秒一次向被升級(jí)的從服務(wù)器發(fā)送info命令(平常是每10秒一次),如果返回的回復(fù)role從原來(lái)的slave變成了master,那么領(lǐng)頭sentinel就知道從服務(wù)器已經(jīng)升級(jí)成主服務(wù)器了。
修改從服務(wù)器的復(fù)制目標(biāo)
通過(guò)SLAVEOF命令來(lái)使從服務(wù)器復(fù)制新的主服務(wù)器。當(dāng)sentinel監(jiān)測(cè)到舊的主服務(wù)器重新上線后,也會(huì)發(fā)送SLAVEOF命令使它成為新的主服務(wù)器的從服務(wù)器。
sentinel總結(jié)
sentinel其實(shí)就是一個(gè)監(jiān)控系統(tǒng),,而sentinel監(jiān)測(cè)到主服務(wù)器下線后,可以通過(guò)選舉機(jī)制選出一個(gè)領(lǐng)頭的sentinel,然后由這個(gè)領(lǐng)頭的sentinel將下線主服務(wù)器下的從服務(wù)器挑選一個(gè)切換成主服務(wù)器,而不用人工手動(dòng)切換。
哨兵模式雖然做到了主從自動(dòng)切換,但是還是只有一臺(tái)主服務(wù)器進(jìn)行寫操作(當(dāng)然哨兵模式也可以監(jiān)視多個(gè)主服務(wù)器,但需要客戶端自己實(shí)現(xiàn)負(fù)載均衡)。官方也提供了自己的方式實(shí)現(xiàn)集群。
節(jié)點(diǎn)
每個(gè)redis服務(wù)實(shí)例就是一個(gè)節(jié)點(diǎn),多個(gè)連接的節(jié)點(diǎn)組成一個(gè)集群。
CLUSTER MEET
向另一個(gè)節(jié)點(diǎn)發(fā)送CLUSTER MEET命令,可以讓節(jié)點(diǎn)與目標(biāo)節(jié)點(diǎn)進(jìn)行握手,握手成功就能將該節(jié)點(diǎn)加入到當(dāng)前集群。
啟動(dòng)節(jié)點(diǎn)
redis服務(wù)器啟動(dòng)時(shí)會(huì)根據(jù)cluster-enabled配置選項(xiàng)是否為yes來(lái)決定是否開(kāi)啟服務(wù)器集群模式。
集群數(shù)據(jù)結(jié)構(gòu)
每個(gè)節(jié)點(diǎn)都會(huì)使用一個(gè)clusterNode結(jié)構(gòu)記錄自己的狀態(tài),并為集群中其他節(jié)點(diǎn)都創(chuàng)建一個(gè)相應(yīng)的clusterNode結(jié)構(gòu),記錄其他節(jié)點(diǎn)狀態(tài)。
typedef struct clusterNode { // 創(chuàng)建節(jié)點(diǎn)的時(shí)間 mstime_t ctime; // 節(jié)點(diǎn)的名稱 char name[CLUSTER_NAMELEN]; // 節(jié)點(diǎn)標(biāo)識(shí) // 各種不同的標(biāo)識(shí)值記錄節(jié)點(diǎn)的角色(比如主節(jié)點(diǎn)或從節(jié)點(diǎn)) // 以及節(jié)點(diǎn)目前所處的狀態(tài)(在線或者下線) int flags; // 節(jié)點(diǎn)當(dāng)前的配置紀(jì)元,用于實(shí)現(xiàn)故障轉(zhuǎn)移 uint64_t configEpoch; // 節(jié)點(diǎn)的ip地址 char ip[NET_IP_STR_LEN]; // 保存建立連接節(jié)點(diǎn)的有關(guān)信息 clusterLink *link; list *fail_reports; // ... } clusterNode;
clusterLink保存著連接節(jié)點(diǎn)所需的相關(guān)信息
typedef struct clusterLink { // ... // 連接的創(chuàng)建時(shí)間 mstime_t ctime; // 與這個(gè)連接相關(guān)聯(lián)的節(jié)點(diǎn),沒(méi)有就為null struct clusterNode *node; // ... } clusterLink;
每個(gè)節(jié)點(diǎn)還保存著一個(gè)clusterState結(jié)構(gòu),它記錄了在當(dāng)前節(jié)點(diǎn)視角下,集群目前所處的狀態(tài),例如集群在線還是下線,集群包含多少個(gè)節(jié)點(diǎn)等等。
typedef struct clusterState { // 指向當(dāng)前節(jié)點(diǎn)clusterNode的指針 clusterNode *myself; // 集群當(dāng)前的配置紀(jì)元,用于實(shí)現(xiàn)故障轉(zhuǎn)移 uint64_t currentEpoch; // 集群當(dāng)前的狀態(tài),上線或者下線 int state; // 集群中至少處理一個(gè)槽的節(jié)點(diǎn)數(shù)量 int size; // 集群節(jié)點(diǎn)的名單(包括myself節(jié)點(diǎn)) // 字典的鍵是節(jié)點(diǎn)的名字,字典的值為節(jié)點(diǎn)對(duì)應(yīng)的clusterNode結(jié)構(gòu) dict *nodes; } clusterState;
CLUSTER MEET 命令的實(shí)現(xiàn)
CLUSTER MEET
節(jié)點(diǎn) A 會(huì)為節(jié)點(diǎn) B 創(chuàng)建一個(gè)clusterNode結(jié)構(gòu),并將該結(jié)構(gòu)添加到自己的clusterState.nodes 字典里面。
之后,節(jié)點(diǎn) A 將根據(jù) CLUSTER MEET 命令給定的 IP 地址和端口號(hào),向節(jié)點(diǎn) B 發(fā)送一條 MEET 消息。
如果一切順利,節(jié)點(diǎn) B 將接收到節(jié)點(diǎn) A 發(fā)送的 MEET 消息,節(jié)點(diǎn) B 會(huì)為節(jié)點(diǎn) A 創(chuàng)建一個(gè)clusterNode結(jié)構(gòu),并將該結(jié)構(gòu)添加到自己的clusterState.nodes字典里面。
之后,節(jié)點(diǎn) B 將向節(jié)點(diǎn) A 返回一條 PONG 消息。
如果一切順利,節(jié)點(diǎn) A 將接收到節(jié)點(diǎn) B 返回的 PONG 消息,通過(guò)這條 PONG 消息節(jié)點(diǎn) A 可以知道節(jié)點(diǎn) B 已經(jīng)成功地接收到了自己發(fā)送的 MEET 消息。
之后,節(jié)點(diǎn) A 將向節(jié)點(diǎn) B 返回一條 PING 消息。
如果一切順利,節(jié)點(diǎn)B將接收到節(jié)點(diǎn)A返回的PING消息,通過(guò)這條PING消息節(jié)點(diǎn)B知道節(jié)點(diǎn)A已經(jīng)成功接收到自己返回的PONG消息,握手完成。
槽指派
集群的整個(gè)數(shù)據(jù)庫(kù)被分為16384個(gè)槽,每個(gè)鍵都屬于16384個(gè)槽的其中一個(gè),集群中每個(gè)節(jié)點(diǎn)處理0個(gè)或16384個(gè)槽。當(dāng)所有的槽都有節(jié)點(diǎn)在處理時(shí),集群處于上線狀態(tài),否則就是下線狀態(tài)。
CLUSTER ADDSLOTS
CLUSTER ADDSLOTS
通過(guò)CLUSTER ADDSLOTS命令可以將指定槽指派給當(dāng)前節(jié)點(diǎn)負(fù)責(zé),例如:CLUSTER ADDSLOTS 0 1 2 3 4 可以將0至4的槽指派給當(dāng)前節(jié)點(diǎn)
記錄節(jié)點(diǎn)的槽指派信息
clusterNode結(jié)構(gòu)的slots屬性和numslot屬性記錄了節(jié)點(diǎn)負(fù)責(zé)處理哪些槽:
typedef struct clusterNode { unsigned char slots[CLUSTER_SLOTS/8]; int numslots; // ... } clusterNode;
slots:
是一個(gè)二進(jìn)制數(shù)組,一共包含16384個(gè)二進(jìn)制位。當(dāng)二進(jìn)制位的值是1,代表節(jié)點(diǎn)負(fù)責(zé)處理該槽,如果是0,代表節(jié)點(diǎn)不處理該槽numslots:
numslots屬性則記錄節(jié)點(diǎn)負(fù)責(zé)處理槽的數(shù)量,也就是slots中值為1的二進(jìn)制位的數(shù)量。
傳播節(jié)點(diǎn)的槽指派信息
節(jié)點(diǎn)除了會(huì)將自己負(fù)責(zé)的槽記錄在clusterNode中,還會(huì)將slots數(shù)組發(fā)送給集群中的其他節(jié)點(diǎn),以此告知其他節(jié)點(diǎn)自己目前負(fù)責(zé)處理哪些槽。
typedef struct clusterState { clusterNode *slots[CLUSTER_SLOTS]; } clusterState;
slots包含16384個(gè)項(xiàng),每一個(gè)數(shù)組項(xiàng)都是指向clusterNode的指針,表示被指派給該節(jié)點(diǎn),如果未指派給任何節(jié)點(diǎn),那么指針指向NULL。
CLUSTER ADDSLOTS命令的實(shí)現(xiàn)
在集群中執(zhí)行命令
客戶端向節(jié)點(diǎn)發(fā)送與數(shù)據(jù)庫(kù)有關(guān)的命令時(shí),接收命令的節(jié)點(diǎn)會(huì)計(jì)算出命令要處理的數(shù)據(jù)庫(kù)鍵屬于哪個(gè)槽,并檢查該槽是否指派給了自己。
如果指派給了自己,那么該節(jié)點(diǎn)直接執(zhí)行該命令。如果沒(méi)有,那么該節(jié)點(diǎn)會(huì)向客戶端返回一個(gè)MOCED的錯(cuò)誤,指引客戶端轉(zhuǎn)向正確的節(jié)點(diǎn),并再次發(fā)送執(zhí)行的命令。
計(jì)算鍵屬于那個(gè)槽
CRC16(key)是計(jì)算出鍵key的CRC16的校驗(yàn)和,而 & 16383就是取余,算出0-16383之間的整數(shù)作為鍵的槽號(hào)。
判斷槽是否由當(dāng)前節(jié)點(diǎn)負(fù)責(zé)處理
計(jì)算出鍵所屬的槽號(hào)i后,節(jié)點(diǎn)就能判斷該槽號(hào)是否由自己處理。
如果clusterState.slots[i]等于如果clusterState.myself,那么由自己負(fù)責(zé)該節(jié)點(diǎn)可以直接執(zhí)行命令。
如果不相等,那么可以獲取clusterState.slots[i]指向如果clusterNode的ip和端口,向客戶端返回MOVED錯(cuò)誤,指引客戶端轉(zhuǎn)向負(fù)責(zé)該槽的節(jié)點(diǎn)。
集群模式下不會(huì)打印MOVED錯(cuò)誤,而是直接自動(dòng)轉(zhuǎn)向。
重新分片
redis集群重新分配可以將任意數(shù)量已經(jīng)指派給某個(gè)節(jié)點(diǎn)的槽改為指派給另一個(gè)節(jié)點(diǎn),相關(guān)槽所屬的鍵值對(duì)也會(huì)從源節(jié)點(diǎn)移動(dòng)到目標(biāo)節(jié)點(diǎn)。
重新分片操作是在線進(jìn)行的,在重新分片的過(guò)程中,集群不用下線,源節(jié)點(diǎn)和目標(biāo)節(jié)點(diǎn)都可以繼續(xù)處理命令請(qǐng)求。
redis集群的重新分片操作是由redis-trib負(fù)責(zé)執(zhí)行。重新分片執(zhí)行步驟如下:
redis-trib對(duì)目標(biāo)節(jié)點(diǎn)發(fā)送CLUSTER SETSLOT
命令,讓目標(biāo)節(jié)點(diǎn)準(zhǔn)備好從源節(jié)點(diǎn)導(dǎo)入槽slot的鍵值對(duì)。
redis-trib對(duì)源節(jié)點(diǎn)發(fā)送CLUSTER SETSLOT
命令,讓源節(jié)點(diǎn)準(zhǔn)備好將屬于槽slot的鍵值對(duì)遷移至目標(biāo)節(jié)點(diǎn)。
redis-trib向源節(jié)點(diǎn)發(fā)送CLUSTER GETKEYSINSLOT
命令,獲取最多count個(gè)屬于槽的鍵值對(duì)的鍵名稱。
對(duì)于步驟3獲取的每個(gè)鍵名,redis-trib都向源節(jié)點(diǎn)發(fā)送一個(gè)MIGRTING
命令,將被選中的鍵值對(duì)從源節(jié)點(diǎn)遷移至目標(biāo)節(jié)點(diǎn)。
重復(fù)執(zhí)行步驟3和步驟4,直到源節(jié)點(diǎn)保存的所以屬于槽slot的鍵值對(duì)都被遷移至目標(biāo)節(jié)點(diǎn)。
redis-trib向集群中任何一個(gè)節(jié)點(diǎn)發(fā)送CLUSTER SETSLOT
命令,將槽指派給目標(biāo)節(jié)點(diǎn)。這一信息最終會(huì)通過(guò)消息發(fā)送至整個(gè)集群。
CLUSTER SETSLOT IMPORTING 命令實(shí)現(xiàn)
typedef struct clusterState { // ... clusterNode *importing_slots_from[CLUSTER_SLOTS]; } clusterState;
importing_slots_from記錄了當(dāng)前節(jié)點(diǎn)正在從其他節(jié)點(diǎn)導(dǎo)入的槽。importing_slots_from[i]不為null,則指向CLUSTER SETSLOT
命令,
CLUSTER SETSLOT MIGRTING 命令實(shí)現(xiàn)
typedef struct clusterState { // ... clusterNode *migrating_slots_to[CLUSTER_SLOTS]; } clusterState;
migrating_slots_to記錄了當(dāng)前節(jié)點(diǎn)正在遷移至其他節(jié)點(diǎn)的槽。migrating_slots_to[i]不為null,則指向遷移至目標(biāo)節(jié)點(diǎn)所代表的clusterNode結(jié)構(gòu)。
ASK錯(cuò)誤
在重新分片期間,源節(jié)點(diǎn)向目標(biāo)節(jié)點(diǎn)遷移槽的過(guò)程中,可能屬于這個(gè)槽的一部分鍵值對(duì)一部分保存在源節(jié)點(diǎn)當(dāng)中,而另一部分保存在目標(biāo)節(jié)點(diǎn)當(dāng)中。
客戶端向源節(jié)點(diǎn)發(fā)送一個(gè)與數(shù)據(jù)庫(kù)鍵有關(guān)的命令,恰好這個(gè)槽正在被遷移。
源節(jié)點(diǎn)現(xiàn)在自己的數(shù)據(jù)庫(kù)中查找指定的鍵,如果找到,直接執(zhí)行。
如果沒(méi)有找到,節(jié)點(diǎn)會(huì)檢查migrating_slots_to[i]查看鍵是否正在遷移,如果在遷移就返回一個(gè)ask錯(cuò)誤,引導(dǎo)客戶端轉(zhuǎn)向目標(biāo)節(jié)點(diǎn)。
ASKING
客戶端收到ask錯(cuò)誤之后,會(huì)先執(zhí)行ASKING命令,再向目標(biāo)節(jié)點(diǎn)發(fā)送命令。ASKING命令就是打開(kāi)發(fā)送該命令的客戶端的REDIS_ASKING標(biāo)識(shí)。一般來(lái)說(shuō)客戶端發(fā)送的鍵如果不屬于自己負(fù)責(zé)會(huì)返回MOVED錯(cuò)誤(槽只遷移部分,這時(shí)槽還不屬于目標(biāo)節(jié)點(diǎn)負(fù)責(zé)),但還會(huì)檢查importing_slots_from[i],如果顯示節(jié)點(diǎn)正在導(dǎo)入槽i,并且發(fā)送命令的客戶端帶有REDIS_ASKING標(biāo)識(shí),那么它就會(huì)破例執(zhí)行一次該命令。
集群的故障轉(zhuǎn)移
集群的故障轉(zhuǎn)移效果和哨兵模式類似,也是將從節(jié)點(diǎn)升級(jí)成主節(jié)點(diǎn)。舊的主節(jié)點(diǎn)重新上線后將會(huì)成為新主節(jié)點(diǎn)的從節(jié)點(diǎn)。
故障檢測(cè)
集群中每個(gè)節(jié)點(diǎn)會(huì)定期的向集群中其他節(jié)點(diǎn)發(fā)送PING消息,檢測(cè)對(duì)方是否在線,如果指定時(shí)間內(nèi)沒(méi)有收到PONG消息,那么就將該節(jié)點(diǎn)標(biāo)記為疑似下線。clusterState.nodes字典中找到該節(jié)點(diǎn)的clusterNode結(jié)構(gòu),將flags屬性修改成REDIS_NODE_PFAIL標(biāo)識(shí)。
集群中各個(gè)節(jié)點(diǎn)會(huì)互相發(fā)送消息來(lái)交換集群中各個(gè)節(jié)點(diǎn)的狀態(tài),例如:主節(jié)點(diǎn)A得知主節(jié)點(diǎn)B認(rèn)為主節(jié)點(diǎn)C進(jìn)入了疑似下線狀態(tài),主節(jié)點(diǎn)A會(huì)在clusterState.nodes字典中找到節(jié)點(diǎn)C的clusterNode結(jié)構(gòu),并將主節(jié)點(diǎn)B的下線報(bào)告添加到clusterNode結(jié)構(gòu)的fail_reports鏈表當(dāng)中。
每一個(gè)下線報(bào)告由一個(gè)clusterNodeFailReport結(jié)構(gòu)表示
typedef struct clusterNodeFailReport { struct clusterNode *node; // 最后一次收到下線報(bào)告的時(shí)間 mstime_t time; } clusterNodeFailReport;
如果一個(gè)集群當(dāng)中,半數(shù)以上負(fù)責(zé)處理槽的主節(jié)點(diǎn)都將某個(gè)主節(jié)點(diǎn)X報(bào)告為疑似下線。那么這個(gè)主節(jié)點(diǎn)X將被標(biāo)記為已下線。將主節(jié)點(diǎn)X標(biāo)記成已下線的節(jié)點(diǎn)會(huì)向集群廣播一條關(guān)于主節(jié)點(diǎn)X的FAIL消息。所有收到這條FAIL消息的節(jié)點(diǎn)都會(huì)將主節(jié)點(diǎn)X標(biāo)記成已下線。
故障轉(zhuǎn)移
當(dāng)一個(gè)從節(jié)點(diǎn)發(fā)現(xiàn)自己正在復(fù)制的主節(jié)點(diǎn)進(jìn)入了已下線狀態(tài),從節(jié)點(diǎn)將開(kāi)始對(duì)下線主節(jié)點(diǎn)進(jìn)行故障轉(zhuǎn)移。
復(fù)制下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn),會(huì)有一個(gè)主節(jié)點(diǎn)被選中。
被選中的從節(jié)點(diǎn)會(huì)執(zhí)行SLAVEOF no one 命令,成為新的主節(jié)點(diǎn)。
新的主節(jié)點(diǎn)會(huì)撤銷所有對(duì)已下線主節(jié)點(diǎn)的槽指派,并將這些槽全部指派給自己。
新的主節(jié)點(diǎn)向集群廣播一條PONG消息,這條PONG消息可以讓集群中的其他節(jié)點(diǎn)立即知道這個(gè)節(jié)點(diǎn)已經(jīng)由從節(jié)點(diǎn)變成主節(jié)點(diǎn)。這個(gè)主節(jié)點(diǎn)已經(jīng)接管了已下線節(jié)點(diǎn)負(fù)責(zé)處理的槽。
新的主節(jié)點(diǎn)開(kāi)始接收和自己負(fù)責(zé)處理的槽有關(guān)的命令請(qǐng)求,故障轉(zhuǎn)移完成。
選舉新的主節(jié)點(diǎn)
新的主節(jié)點(diǎn)通過(guò)選舉產(chǎn)生
集群的配置紀(jì)元是一個(gè)自增計(jì)數(shù)器,它的初始值為0。
當(dāng)集群的某個(gè)節(jié)點(diǎn)開(kāi)始一次故障轉(zhuǎn)移操作,集群的配置紀(jì)元的值加1。
對(duì)于每個(gè)配置紀(jì)元,集群里每個(gè)負(fù)責(zé)處理槽的主節(jié)點(diǎn)都有一次投票的機(jī)會(huì),第一個(gè)想主節(jié)點(diǎn)要求投票的從節(jié)點(diǎn)將獲得主節(jié)點(diǎn)的投票。
當(dāng)從節(jié)點(diǎn)發(fā)現(xiàn)自己正在復(fù)制的主節(jié)點(diǎn)進(jìn)入已下線狀態(tài)時(shí),從節(jié)點(diǎn)會(huì)向集群廣播一條CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這條消息,并具有投票權(quán)的主節(jié)點(diǎn)向這個(gè)從節(jié)點(diǎn)投票。
如果一個(gè)主節(jié)點(diǎn)具有投票權(quán)(它正在負(fù)責(zé)處理槽),并且這個(gè)主節(jié)點(diǎn)尚未投票給其他從節(jié)點(diǎn),那么主節(jié)點(diǎn)將向要求投票的從節(jié)點(diǎn)返回一條CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示這個(gè)主節(jié)點(diǎn)支持從節(jié)點(diǎn)成為新的主節(jié)點(diǎn)。
每個(gè)參與選舉的從節(jié)點(diǎn)都會(huì)接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根據(jù)自己收到了多少條這種消息來(lái)統(tǒng)計(jì)自己獲得了多少主節(jié)點(diǎn)的支持。
如果集群里有 N 個(gè)具有投票權(quán)的主節(jié)點(diǎn),那么當(dāng)一個(gè)從節(jié)點(diǎn)收集到大于等于 N / 2 + l 張支持票時(shí),這個(gè)從節(jié)點(diǎn)就會(huì)當(dāng)選為新的主節(jié)點(diǎn)。
因?yàn)樵诿恳粋€(gè)配置紀(jì)元里面,每個(gè)具有投票權(quán)的主節(jié)點(diǎn)只能投一次票,所以如果有 N 個(gè)主節(jié)點(diǎn)進(jìn)行投票,那么具有大于等于 N / 2 + l 張支持票的從節(jié)點(diǎn)只會(huì)有一個(gè),這確保了新的主節(jié)點(diǎn)只會(huì)有一個(gè)。
如果在一個(gè)配置紀(jì)元里面沒(méi)有從節(jié)點(diǎn)能收集到足夠多的支持票,那么集群進(jìn)人一個(gè)新的配置紀(jì)元,并再次進(jìn)行選舉,直到選出新的主節(jié)點(diǎn)為止。
主節(jié)點(diǎn)選舉的過(guò)程和選舉領(lǐng)頭sentinel的過(guò)程非常相似。
主從復(fù)制數(shù)據(jù)丟失
主從復(fù)制之間是異步執(zhí)行的,有可能master的部分?jǐn)?shù)據(jù)還沒(méi)來(lái)得及同步到從數(shù)據(jù)庫(kù),然后master就掛了,這時(shí)這部分未同步的數(shù)據(jù)就丟失了。
腦裂
腦裂就是說(shuō),某個(gè)master所在機(jī)器突然脫離了正常的網(wǎng)絡(luò),跟其他slave機(jī)器不能連接,但是實(shí)際上master還運(yùn)行著。此時(shí)哨兵可能就會(huì)認(rèn)為master 宕機(jī)了,然后開(kāi)啟選舉,將其他slave切換成了master,這個(gè)時(shí)候,集群里面就會(huì)有2個(gè)master,也就是所謂的腦裂。
此時(shí)雖然某個(gè)slave被切換成了master,但是可能client還沒(méi)來(lái)得及切換到新的master,還繼續(xù)向舊master的寫數(shù)據(jù)。
master再次恢復(fù)的時(shí)候,會(huì)被作為一個(gè)slave掛到新的master上去,自己的數(shù)據(jù)將會(huì)清空,重新從新的master復(fù)制數(shù)據(jù),導(dǎo)致數(shù)據(jù)丟失。
減少數(shù)據(jù)丟失的配置
min-slaves-to-writ 1 min-slaves-max-lag 10
上述配置表示,如果至少有1個(gè)從服務(wù)器超過(guò)10秒沒(méi)有給自己ack消息,那么master不再執(zhí)行寫請(qǐng)求。
當(dāng)從數(shù)據(jù)庫(kù)因?yàn)榫W(wǎng)絡(luò)原因或者執(zhí)行復(fù)雜度高命令阻塞導(dǎo)致滯后執(zhí)行同步命令,導(dǎo)致數(shù)據(jù)同步延遲,造成了主從數(shù)據(jù)庫(kù)不一致。
感謝大家的閱讀,以上就是“如何分析redis中的高可用方案”的全部?jī)?nèi)容了,學(xué)會(huì)的朋友趕緊操作起來(lái)吧。相信創(chuàng)新互聯(lián)小編一定會(huì)給大家?guī)?lái)更優(yōu)質(zhì)的文章。謝謝大家對(duì)創(chuàng)新互聯(lián)網(wǎng)站的支持!