高可用 --- Redis

因為Redis擁有諸多優(yōu)秀的特性,使用范圍越來越廣彪见,系統(tǒng)對其可用性的依賴也越來越重乾忱,當前絕大部分系統(tǒng)使用的Redis都實現(xiàn)了高可用屉更。這里主要介紹Redis官方推薦的兩種高可用方案Sentinel和Redis Cluster彤守。

CAP選擇

CAP理論:一個分布式系統(tǒng)最多只能同時滿足一致性(Consistency)、可用性(Availability)和分區(qū)容錯性(Partition tolerance)這三項中的兩項哭靖。
Redis選擇了AP具垫,犧牲了C。

復制/同步的實現(xiàn)

任何分布式系統(tǒng)试幽,要滿足分區(qū)容錯性筝蚕,必須實現(xiàn)數(shù)據(jù)的復制/同步功能。

SYNC

從服務器對主服務器的同步操作需要通過向主服務器發(fā)送SYNC命令來完成铺坞,以下是SYNC命令的執(zhí)行步驟:

  1. 從服務器向主服務器發(fā)送SYNC命令饰及。
  2. 收到SYNC命令的主服務器執(zhí)行BGSAVE命令,在后臺生成一個RDB文件康震,并使用一個緩沖區(qū)記錄從現(xiàn)在開始執(zhí)行的所有寫命令燎含。
  3. 當主服務器的BGSAVE命令執(zhí)行完畢時,主服務器會將BGSAVE命令生成的RDB文件發(fā)送給從服務器腿短,從服務器接收并載入這個RDB文件屏箍,將自己的數(shù)據(jù)庫狀態(tài)更新至主服務器執(zhí)行BGSAVE命令時的數(shù)據(jù)庫狀態(tài)。
  4. 主服務器將記錄在緩沖區(qū)里面的所有寫命令發(fā)送給從服務器橘忱,從服務器執(zhí)行這些寫命令赴魁,將自己的數(shù)據(jù)庫狀態(tài)更新至主服務器數(shù)據(jù)庫當前所處的狀態(tài)。


    主從服務器的同步過程

從服務器斷開重連后钝诚,即使只有很小部分數(shù)據(jù)沒有同步颖御,也需要重新整個同步過來,而SYNC命令是一個非常耗費資源的操作凝颇,所以Redis有必要保證在真正有需要時才執(zhí)行SYNC命令潘拱。 所以從Redis2.8開始,使用PSYNC代替SYNC執(zhí)行主從同步功能拧略。

PSYNC

PSYNC命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)兩種模式:

  • 其中完整重同步用于處理初次復制情況:完整重同步的執(zhí)行步驟和SYNC命令的執(zhí)行步驟基本一樣芦岂,它們都是通過讓主服務器創(chuàng)建并發(fā)送RDB文件,以及向從服務器發(fā)送保存在緩沖區(qū)里面的寫命令來進行同步垫蛆。
  • 而部分重同步則用于處理斷線后重復制情況:當從服務器在斷線后重新連接主服務器時禽最,如果條件允許,主服務器可以將主從服務器連接斷開期間執(zhí)行的寫命令發(fā)送給從服務器袱饭,從服務器只要接收并執(zhí)行這些寫命令川无,就可以將數(shù)據(jù)庫更新至主服務器當前所處的狀態(tài)。


    使用PSYNC命令來進行斷線后重復制

部分重同步的功能由一下三個部分構(gòu)成:

  • 主服務器的復制偏移量(replication offset)和從服務器的復制偏移量
  • 主服務器的復制積壓緩沖區(qū)(replication backlog)
  • 服務器的運行ID(run ID)

從這三個部分構(gòu)成應該很容易就能夠判斷出來部分重同步功能的實現(xiàn)邏輯虑乖。(如有不明白可以參考《Redis設計與實現(xiàn)》)

高可用

Redis實現(xiàn)高可用主要有兩種方式懦趋,一種是Sentinel(3.0之前),一種是3.0正式支持的Redis Cluster(推薦)决左。

Sentinel(哨兵)

在Redis Server之外愕够,運行數(shù)個Sentinel(一般是3個)走贪,負責監(jiān)控Redis Server健康狀況以及實現(xiàn)故障轉(zhuǎn)移。在Sentinel模式下惑芭,Redis Server一般具有1個主節(jié)點及多個從節(jié)點坠狡,Sentinel會與所有的Redis節(jié)點以及其他Sentinel保持連接。 具體工作模式如下:

檢測主觀下線狀態(tài)

在默認情況下遂跟,Sentinel會以每秒一次的頻率向所有與它創(chuàng)建了命令連接的實例(包括主服務器逃沿、從服務器、其他Sentinel在內(nèi))發(fā)送PING命令幻锁,并通過實例返回的PING命令回復來判斷實例是否在線凯亮。如果連續(xù)返回無效回復,則表示這個實例已經(jīng)進入主觀下線狀態(tài)哄尔。

可以修改down-after-milliseconds來控制判斷主觀下線的所需要的時間長度

檢查客觀下線狀態(tài)

當Sentinel將一個主服務器判斷為主觀下線之后假消,為了確認這個主服務器是否真的下線了,它會向同樣監(jiān)視這一主服務器的其他Sentinel進行詢問岭接,看它們是否也認為主服務器已經(jīng)進入了下線狀態(tài)(可以是主觀下線或者客觀下線)富拗。當Sentinel從其他Sentinel那里接收到足夠數(shù)量的已下線判斷之后,Sentinel就會將從服務器判定為客觀下線鸣戴,并對主服務器執(zhí)行故障轉(zhuǎn)移操作啃沪。

選舉領頭Sentinel

以下是Redis選舉領頭Sentinel的規(guī)則和方法:

  • 所有在線的Sentinel都有被選為領頭Sentinel的資格,換句話說窄锅,監(jiān)視同一個主服務器的多個在線Sentinel中的任意一個都有可能成為領頭Sentinel创千。
  • 每次進行領頭Sentinel選舉之后,不論選舉是否成功入偷,所有Sentinel的配置紀元(configuration epoch)的值都會自增一次追驴。配置紀元實際上就是一個計數(shù)器,并沒有什么特別的盯串。
  • 在一個配置紀元里面氯檐,所有Sentinel都有一次將某個Sentinel設置為局部領頭Sentinel的機會,并且局部領頭一旦設置体捏,在這個配置紀元里面就不能再更改。
  • 每個發(fā)現(xiàn)主服務器進入客觀下線的Sentinel都會要求其他Sentinel將自己設置為局部領頭Sentinel糯崎。
  • 當一個Sentinel(源Sentinel)向另一個Sentinel(目標Sentinel)發(fā)送SENTINEL is-master-down-by-addr命令几缭,并且命令中的runid參數(shù)不是*符號而是源Sentinel的運行ID時,這表示源Sentinel要求目標Sentinel將前者設置為后者的局部領頭Sentinel沃呢。
  • Sentinel設置局部領頭Sentinel的規(guī)則是先到先得:最先向目標Sentinel發(fā)送設置要求的源Sentinel將成為目標Sen-tinel的局部領頭Sentinel年栓,而之后接收到的所有設置要求都會被目標Sentinel拒絕。
  • 目標Sentinel在接收到SENTINEL is-master-down-by-addr命令之后薄霜,將向源Sentinel返回一條命令回復某抓,回復中的leader_runid參數(shù)和leader_epoch參數(shù)分別記錄了目標Sentinel的局部領頭Sentinel的運行ID和配置紀元纸兔。
  • 源Sentinel在接收到目標Sentinel返回的命令回復之后,會檢查回復中l(wèi)eader_epoch參數(shù)的值和自己的配置紀元是否相同否副,如果相同的話汉矿,那么源Sentinel繼續(xù)取出回復中的leader_runid參數(shù),如果leader_runid參數(shù)的值和源Sen-tinel的運行ID一致备禀,那么表示目標Sentinel將源Sentinel設置成了局部領頭Sentinel洲拇。
  • 如果有某個Sentinel被半數(shù)以上的Sentinel設置成了局部領頭Sentinel,那么這個Sentinel成為領頭Sentinel曲尸。舉個例子赋续,在一個由10個Sentinel組成的Sentinel系統(tǒng)里面,只要有大于等于10/2+1=6個Sentinel將某個Sentinel設置為局部領頭Sentinel另患,那么被設置的那個Sentinel就會成為領頭Sentinel纽乱。
  • 因為領頭Sentinel的產(chǎn)生需要半數(shù)以上Sentinel的支持,并且每個Sentinel在每個配置紀元里面只能設置一次局部領頭Sentinel昆箕,所以在一個配置紀元里面鸦列,只會出現(xiàn)一個領頭Sentinel。
  • 如果在給定時限內(nèi)为严,沒有一個Sentinel被選舉為領頭Sen-tinel敛熬,那么各個Sentinel將在一段時間之后再次進行選舉,直到選出領頭Sentinel為止第股。

Sentinel系統(tǒng)選舉領頭Sentinel的方法是對Raft算法的領頭選舉方法的實現(xiàn)应民。

故障轉(zhuǎn)移

在選舉產(chǎn)生出領頭Sentinel之后,領頭Sentinel將對已下線的主服務器執(zhí)行故障轉(zhuǎn)移操作夕吻,該操作包含以下三個步驟:

  1. 在已下線主服務器屬下的所有從服務器里面诲锹,挑選出一個從服務器,并將其轉(zhuǎn)換為主服務器涉馅。
  2. 讓已下線主服務器屬下的所有從服務器改為復制新的主服務器归园。
  3. 將已下線主服務器設置為新的主服務器的從服務器,當這個舊的主服務器重新上線時稚矿,它就會成為新的主服務器的從服務器庸诱。

Redis Cluster

當一個從節(jié)點發(fā)現(xiàn)自己正在復制的主節(jié)點進入了已下線狀態(tài)時,從節(jié)點將開始對下線主節(jié)點進行故障轉(zhuǎn)移晤揣,以下是故障轉(zhuǎn)移的執(zhí)行步驟:

  1. 復制下線主節(jié)點的所有從節(jié)點里面桥爽,會有一個從節(jié)點被選中。
  2. 被選中的從節(jié)點會執(zhí)行SLAVEOF no one命令昧识,成為新的主節(jié)點钠四。
  3. 新的主節(jié)點會撤銷所有對已下線主節(jié)點的槽指派,并將這些槽全部指派給自己跪楞。
  4. 新的主節(jié)點向集群廣播一條PONG消息缀去,這條PONG消息可以讓集群中的其他節(jié)點立即知道這個節(jié)點已經(jīng)由從節(jié)點變成了主節(jié)點侣灶,并且這個主節(jié)點已經(jīng)接管了原本由已下線節(jié)點負責處理的槽。
  5. 新的主節(jié)點開始接收和自己負責處理的槽有關的命令請求缕碎,故障轉(zhuǎn)移完成褥影。

選舉新的主節(jié)點

新的主節(jié)點是通過選舉產(chǎn)生的。以下是集群選舉新的主節(jié)點的方法:

  1. 集群的配置紀元是一個自增計數(shù)器阎曹,它的初始值為0伪阶。
  2. 當集群里的某個節(jié)點開始一次故障轉(zhuǎn)移操作時,集群配置紀元的值會被增一处嫌。
  3. 對于每個配置紀元栅贴,集群里每個負責處理槽的主節(jié)點都有一次投票的機會,而第一個向主節(jié)點要求投票的從節(jié)點將獲得主節(jié)點的投票熏迹。
  4. 當從節(jié)點發(fā)現(xiàn)自己正在復制的主節(jié)點進入已下線狀態(tài)時檐薯,從節(jié)點會向集群廣播一條CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這條消息注暗、并且具有投票權的主節(jié)點向這個從節(jié)點投票坛缕。
  5. 如果一個主節(jié)點具有投票權(它正在負責處理槽),并且這個主節(jié)點尚未投票給其他從節(jié)點捆昏,那么主節(jié)點將向要求投票的從節(jié)點返回一條CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息赚楚,表示這個主節(jié)點支持從節(jié)點成為新的主節(jié)點。
  6. 每個參與選舉的從節(jié)點都會接收CLUS-TERMSG_TYPE_FAILOVER_AUTH_ACK消息骗卜,并根據(jù)自己收到了多少條這種消息來統(tǒng)計自己獲得了多少主節(jié)點的支持宠页。
  7. 如果集群里有N個具有投票權的主節(jié)點,那么當一個從節(jié)點收集到大于等于N/2+1張支持票時寇仓,這個從節(jié)點就會當選為新的主節(jié)點举户。
  8. 因為在每一個配置紀元里面,每個具有投票權的主節(jié)點只能投一次票遍烦,所以如果有N個主節(jié)點進行投票俭嘁,那么具有大于等于N/2+1張支持票的從節(jié)點只會有一個,這確保了新的主節(jié)點只會有一個服猪。
  9. 如果在一個配置紀元里面沒有從節(jié)點能收集到足夠多的支持票供填,那么集群進入一個新的配置紀元,并再次進行選舉罢猪,直到選出新的主節(jié)點為止捕虽。

這個選舉新主節(jié)點的方法和選舉領頭Sentinel的方法非常相似,因為兩者都是基于Raft算法的領頭選舉(leader election)方法來實現(xiàn)的坡脐。

注意事項

  • 因為Sentinel與Redis Cluster都沒有實現(xiàn)強一致性(也沒有實現(xiàn)最終一致性),所以在使用時房揭,要牢記這一點备闲,不能用在一致性要求特別高的場景晌端,比如全局唯一ID,交易數(shù)據(jù)等恬砂。
  • 如果master沒有設置持久化咧纠,存在風險,如果不小心重啟泻骤,則會丟失所有數(shù)據(jù)漆羔,而且從機也會因為同步,丟失所有數(shù)據(jù)(所以一定要高可用)狱掂。就算使用Sentinel也可能存在問題演痒,因為master可能會在很短時間的恢復,從而Sentinel根本沒有來得及檢測到并切換主從趋惨。 如果真的為了極致性能鸟顺,關閉了master的持久化,就一定要關閉自動重啟功能器虾。

參考

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末讯嫂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子兆沙,更是在濱河造成了極大的恐慌,老刑警劉巖葛圃,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異装悲,居然都是意外死亡,警方通過查閱死者的電腦和手機诀诊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門洞渤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來属瓣,“玉大人,你說我怎么就攤上這事抡蛙。” “怎么了粗截?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我绽榛,道長湿酸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任灭美,我火速辦了婚禮推溃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘届腐。我一直安慰自己铁坎,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布犁苏。 她就那樣靜靜地躺著硬萍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪傀顾。 梳的紋絲不亂的頭發(fā)上襟铭,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音短曾,去河邊找鬼寒砖。 笑死,一個胖子當著我的面吹牛嫉拐,可吹牛的內(nèi)容都是我干的哩都。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼婉徘,長吁一口氣:“原來是場噩夢啊……” “哼漠嵌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起盖呼,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤儒鹿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后几晤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體约炎,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年蟹瘾,在試婚紗的時候發(fā)現(xiàn)自己被綠了圾浅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡憾朴,死狀恐怖狸捕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情众雷,我是刑警寧澤灸拍,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布做祝,位于F島的核電站,受9級特大地震影響株搔,放射性物質(zhì)發(fā)生泄漏剖淀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一纤房、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翻诉,春花似錦炮姨、人聲如沸舒岸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至壳澳,卻和暖如春茫经,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卸伞。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工荤傲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氨菇。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓查蓉,卻偏偏與公主長得像榜贴,于是被迫代替她去往敵國和親妹田。 傳聞我的和親對象是個殘疾皇子鬼佣,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容