1. 程式人生 > >主從哨兵叢集終於給你說明白了

主從哨兵叢集終於給你說明白了

前言碎語

說起 Redis 應該沒有人會陌生了吧,作為開發中最最最最最最最常用的 nosql,它的重要性不言而喻。

Redis有三種叢集模式,第一個就是主從模式,第二種“哨兵”模式,第三種是 Cluster 叢集模式。

今天就和大家細細聊聊這三種模式。

主從複製

當其中一臺伺服器更新之後,伺服器會自動的將這臺更新的資料同步到另外一臺伺服器上。

通過持久化的功能,redis可以保證就算是服務宕機重啟了,也只有少量的資料會丟失。但是在真實的使用場景當中,如果真的只有一臺伺服器,並且恰好宕機了,那麼就會導致整個服務都不可用,因此redis提供了叢集的方式來部署,可以避免這種問題。

在主從複製這種叢集部署模式中,我們會將資料庫分為兩類,第一種稱為主資料庫(master),另一種稱為從資料庫(slave)。

主資料庫會負責我們整個系統中的讀寫操作,從資料庫會負責我們整個資料庫中的讀操作。

其中在職場開發中的真實情況是,我們會讓主資料庫只負責寫操作,讓從資料庫只負責讀操作,就是為了讀寫分離,減輕伺服器的壓力。

但是我在實際開發中會遇到一種情況,該資料是個熱點資料,我們知道,資料同步一定是會耗時的,那麼當一個熱點資料進入master中,而slave沒有來得及更新,再去讀這個資料就會造成資料不一致現象,所以當時我的方案就是直接去讀master節點,這個邏輯同樣適用於mysql主從中出現的問題。

主從同步原理

  • 當一個從資料庫啟動時,它會向主資料庫傳送一個SYNC命令
  • master收到後,在後臺儲存快照,也就是我們說的RDB持久化,當然儲存快照是需要消耗時間的,並且redis是單執行緒的(redis後面也支援了多執行緒,這裡我們先不講),在儲存快照期間redis收到的命令會快取起來,快照完成後會將快取的命令以及快照一起打包發給slave節點,從而保證主從資料庫的一致性。
  • 從資料庫接受到快照以及快取的命令後會將這部分資料寫入到硬碟上的臨時檔案當中,寫入完成後會用這份檔案去替換掉RDB快照檔案,當然,這個操作是不會阻塞的,可以繼續接收命令執行,具體原因其實就是fork了一個子程序,用子程序去完成了這些功能。

因為不會阻塞,所以,這部分初始化完成後,當主資料庫執行了改變資料的命令後,會非同步的給slave,這也就是我們說的複製同步階段,這個階段會貫穿在整個主從同步的過程中,直到主從同步結束後,複製同步才會終止。

那麼我上文提到的資料不一致的現象又是怎麼回事呢?

是因為redis採用了樂觀複製的策略:

容忍一定時間內主從資料庫的資料是不一致的,但是會保證最終的結果一致。

所以當主從複製發生時,正常情況下的命令都會在主資料庫完成,然後直接反回給客戶端,這樣我們的效能就不會受到影響了,因為這裡是主資料庫先完成命令,那麼就會產生其他問題。

舉個例子,假如現在有1個master,6個slave,現在只有兩個slave完成了同步,master寫了新命令,在master準備將此命令傳輸給其他slave時,此刻其他的slave斷電了,那麼就會造成資料不一致的現象發生。

所以redis針對這種情況作了兩個配置

min-slaves-to-write    2  
(只有2個及以上的從資料庫連線到了主資料庫時,master庫才是可寫的)
min-slaves-max-lag   10 
(10秒slave沒有和master進行互動就認為丟失連結)

無硬碟複製

我們剛剛說了主從之間是通過RDB快照來互動的,雖然看來邏輯很簡單,但是還是會存在一些問題:

  • 1.master禁用了RDB快照時,發生了主從同步(複製初始化)操作,也會生成RDB快照,但是之後如果master發成了重啟,就會用RDB快照去恢復資料,這份資料可能已經很久了,中間就會丟失資料
  • 2.在這種一主多從的結構中,master每次和slave同步資料都要進行一次快照,從而在硬碟中生成RDB檔案,會影響效能

為了解決這種問題,redis在後續的更新中也加入了無硬碟複製功能,也就是說直接通過網路傳送給slave,避免了和硬碟互動,但是也是有io消耗的。

增量複製

為什麼會有增量複製?

剛剛我們說了複製的原理,但是他的缺點是很明顯的,就是在斷開主從連結後,即使你只發生了一條資料變化,也需要將所有的資料通過SYNC命令用RDB將所有的資料同步給slave,但是其實並不需要同步所有的資料,只需要將改變的這小部分資料同步給slave就好了

所以為了解決這個問題,redis就有了增量複製。

這個原理其實是很簡單的,學過kafka 的小夥伴應該知道,kafka消費是通過偏移量來計算的,redis的增量複製也是如此。

master會記下每個slave的id,在複製期間,如果有新訊息,會將新訊息(其實是新的命令,當然只包括讓資料放生變動的命令,如 set 這種 )存放在一個固定大小的迴圈佇列中,這個大小是可以配置的,當然這時候傳送的就是PSYNC命令了,然後master會在複製完成後將這部分資料傳送給slave,這樣就在很大程度上保證了資料一致性。

哨兵模式

上文咱們說主從複製,在這種一主多從的結構中,我們讓主從資料庫做到了讀寫分離,也讓從資料庫能夠完成資料備份的功能,可是也留下了一個比較嚴重的問題,當master掛了之後,只能由運維人員重新選擇一個slave升級成master,然後繼續提供服務。

想想一下,你國慶正放假,躺在三亞的海邊沐浴著陽光,享受著香檳,突然你們boss給你來了個電話,說線上的master掛了,是不是會心裡一句mmp(嗯,我這是文明用語)???,所以,redis為了你考慮,在redis2.6版本中,他來了他來了--------哨兵模式

什麼是哨兵?

顧名思義,哨兵其實就是放哨的,它主要會有完成兩個功能。

  • 1.監控整個主資料庫和從資料庫,觀察它們是否正常執行

  • 2.當主資料庫發生異常時,自動的將從資料庫升級為主資料庫,繼續保證整個服務的穩定

哨兵其實是一個獨立的程序,如下圖

當然,上圖只是一個哨兵存在時的情況,但在現實中還會有兩個,甚至更多哨兵存在的情況

實現原理

當一個哨兵程序啟動時,它會先通過配置檔案,找我們的主資料庫,當然,我們這裡也只需要配置其監控的主資料庫就好,之後哨兵會自動發現所有複製該主資料庫的從資料庫,當然一個哨兵是可以監控多個redis系統的,同時,多個哨兵也可以同時監控一個redis系統的,這裡moon先給大家灌輸下這個概念,大家理解下,詳細的我會在後文提到。

哨兵程序啟動後後會和master建立兩條連結

  • 1.用來獲取其他同樣在監控著此redis系統的哨兵資訊
  • 2.傳送一個info命令來獲取此redis系統master本身的資訊

當和master完成連結建立後,該哨兵就會定時的做以下三件事情

  • 1.每10秒會向master和slave傳送info命令
  • 2.每2秒會向master和slave傳送自己的資訊
  • 3.每1秒會向master,slave以及其他同樣在監控著此redis系統的哨兵傳送ping命令

以上三個操作可是說是哨兵的核心了,下面就著重介紹一下這三個命令

首先,info命令可以讓哨兵獲取到當前資料庫的資訊,比如執行id,複製資訊等等,從而實現新節點的自動發現,從資料庫的資訊正是從info命令中獲取的,獲取從資料庫資訊後,就會和從資料庫建立兩條連結,和主資料庫建立的連結是完全一樣的,之後就會每10s向主從資料庫傳送info命令,當有新的從資料庫加入時,就會從info命令中發現了,從而將這個新的slave加入自己的監控列表中。

當然如果有新的哨兵加入到了監控中,其他哨兵也是從這個info命令中獲取的。

於此,就完成了對資料庫以及其他哨兵的自動發現和監控,是不是很easy呢??

以上講了自動發現數據庫和其他的哨兵節點,之後哨兵就開始了它的工作,就是去監控這些資料庫和節點有沒有停止,哨兵就會每隔一段時間向這些節點發送PING命令,如果一段時間沒有收到回覆後,那麼這個哨兵就會認為該節點已經掛了,我們將其稱為主觀下線。

如果該節點是master,哨兵就會向其他節點詢問,看其他節點時候也認為該master掛了,我們可以認為他們在投票,當票數達到了一定的次數,那麼哨兵就認為該節點真的掛了,我們成為客觀下線,然後哨兵之間就會選舉,選出一個領頭的哨兵對主從資料庫發起故障的修復。

哨兵選舉過程

  • 1.第一個發現該master掛了的哨兵,向每個哨兵傳送命令,讓對方選舉自己成為領頭哨兵
  • 2.其他哨兵如果沒有選舉過他人,就會將這一票投給第一個發現該master掛了的哨兵
  • 3.第一個發現該master掛了的哨兵如果發現由超過一半哨兵投給自己,並且其數量也超過了設定的quoram引數,那麼該哨兵就成了領頭哨兵
  • 4.如果多個哨兵同時參與這個選舉,那麼就會重複該過程,知道選出一個領頭哨兵

選出領頭哨兵後,就開始了故障修復,會從選出一個從資料庫作為新的master

master選舉過程

  • 1.從所有線上的從資料庫中,選擇優先順序最高的從資料庫
  • 2.如果有多個優先順序高的從資料庫,那麼就會判斷其偏移量,選擇偏移量最小的從資料庫,這裡的偏移量就是增量複製的
  • 3.如果還是有相同條件的從資料庫,就會選擇執行id較小的從資料庫升級為master

cluster叢集模式

在redis3.0版本中支援了cluster叢集部署的方式,這種叢集部署的方式能自動將資料進行分片,每個master上放一部分資料,提供了內建的高可用服務,即使某個master掛了,服務還可以正常地提供,我們先來看張圖:

使用cluster叢集模式,只需要將每個資料庫節點的cluster-enabled配置選項開啟即可,但是每個cluster叢集至少要保證有3個主資料庫才能正常執行。

cluster叢集模式是怎麼存放資料的?

一個cluster叢集中總共有16384個節點,叢集會將這16384個節點平均分配給每個節點,當然,我這裡的節點指的是每個主節點,就如同下圖:

鍵是如何和16384個插槽做關聯的?

redis將每個redis的鍵的鍵名有效部分使用CRC16演算法計算出雜湊值,然後與16384取餘數,這樣的就可以使每個鍵能夠儘量的均勻分佈在16384個插槽中。

插槽是如何和節點做關聯的?

  • 1.插槽之前沒有被分配過,現在想分配給指定節點
  • 2.插槽之前被分配過,現在想移動指定節點

第一種情況可以通過cluster add slot s 命令來實現

第二種情況的原理相對麻煩一點,但是redis也提供的便捷的方式去操作,我們可以使用redis-trib.rb去實現

如何獲取與插槽對應的節點?

當客戶端向redis叢集中的任意一個節點發送命令後,該節點都會判斷當前鍵的資訊是否存在於當前節點:

  • 如果存在,那麼就會像單機的reids一樣執行命令。

  • 如果不存在,就會返回一個move重定向請求,告訴客戶端負責該資料的節點是哪一個,然後客戶端會向該節點發送命令再次請求獲取資料

新節點的加入

需要通過cluster meet命令來實現:

cluster meet ip port

ip port 是我們已執行的redis叢集中任意一個節點的地址和埠號,新節點在客戶端輸入命令後,會與命令中的節點進行握手,握手後,命令中的叢集節點會將這個新節點的資訊分享給叢集中的每一個節點。

故障恢復

判斷故障的邏輯其實與哨兵模式有點類似,在叢集中,每個節點都會定期的向其他節點發送ping命令,通過有沒有收到回覆來判斷其他節點是否已經下線。

如果長時間沒有回覆,那麼發起ping命令的節點就會認為目標節點疑似下線,也可以和哨兵一樣稱作主觀下線,當然也需要叢集中一定數量的節點都認為該節點下線才可以,我們來說說具體過程:

  • 1。當A節點發現目標節點疑似下線,就會向叢集中的其他節點散播訊息,其他節點就會向目標節點發送命令,判斷目標節點是否下線
  • 2.如果叢集中半數以上的節點都認為目標節點下線,就會對目標節點標記為下線,從而告訴其他節點,讓目標節點在整個叢集中都下線

如何提高redis的讀寫能力

這個問題也是我們之前丟擲來的問題,我們放一張圖大家就會很容易明白了:

提高寫能力只需要橫向擴容master

提高讀能力只需要橫向擴容slave

結語

關於這三種部署的方式,基本上在我知道的公司都毫無疑問直接選擇cluster模式,當然具體的選擇還是要看公司的規模了,畢竟技術服務於業務,選擇合適於當前業務的,就是最好的。

下期