關於Redis的幾件小事 | 高併發和高可用

飛翔碼農發表於2019-07-23

如果你用redis快取技術的話,肯定要考慮如何用redis來加多臺機器,保證redis是高併發的,還有就是如何讓Redis保證自己不是掛掉以後就直接死掉了。

 

redis高併發:主從架構,一主多從,一般來說,很多專案其實就足夠了,單主用來寫入資料,單機幾萬QPS,多從用來查詢資料,多個從例項可以提供每秒10萬的QPS。

 

redis高併發的同時,還需要容納大量的資料:一主多從,每個例項都容納了完整的資料,比如redis主就10G的記憶體量,其實你就最對只能容納10g的資料量。如果你的快取要容納的資料量很大,達到了幾十g,甚至幾百g,或者是幾t,那你就需要redis叢集,而且用redis叢集之後,可以提供可能每秒幾十萬的讀寫併發。

 

redis高可用:如果你做主從架構部署,其實就是加上哨兵就可以了,就可以實現,任何一個例項當機,自動會進行主備切換。

下面詳細介紹。

 

一.redis如何通過讀寫分離來承載讀請求QPS超過10萬+?

 

1.redis高併發跟整個系統高併發的關係

Rredis要搞高併發,那就要把底層的快取搞好,讓更少的請求直接到資料庫,因為資料庫的高併發實現起來是比較麻煩的,而且有些操作還有事務的要求等等,所以很難做到非常高的併發。

Redis併發做的好對於整個系統的併發來說還是不夠的,但是redis作為整個大型的快取架構,在支撐高併發的架構裡面是非常重要的一環。

要實現系統的高併發,首先快取中介軟體、快取系統必須要能夠支撐起高併發,然後在經過良好的整體快取架構設計(多級快取、熱點快取),才能真正支撐起高併發。

 

2.redis不能支撐高併發的瓶頸

Redis不能支撐高併發的瓶頸主要是單機問題,也就是說只有一個單一的redis,就算機器效能再怎麼好,也是有上限的。

 

3.如何支撐更高的併發

單機的redis不可能支撐太高的併發量,要想支援更高的併發可以進行 讀寫分離 。對於快取來說,一般都是支撐讀高併發的,寫的請求是比較少的,因此可以基於主從架構進行讀寫分離。

 

配置一個master(主)機器用來寫入資料,配置多個slave(從)來進行資料的讀取,在master接收到資料之後將資料同步到slave上面即可,這樣slave可以配置多臺機器,就可以提高整體的併發量。

 

二.redis replication以及master持久化對主從架構的安全意義

 

1.redis replication原理

一個master節點下面掛若干個slave節點,寫操作將資料寫到master節點上面去,然後在master寫完之後,通過非同步操作的方式將資料同步到所有的slave節點上面去,保證所有節點的資料是一致的。

 

2.redis replication的核心機制

(1)redis採用非同步方式複製資料到slave節點,不過redis2.8開始,slave node會週期性地確認自己每次複製的數量。

(2)一個master node 可以配置多個 salve node。

(3)slave node也可以連線其他的slave node。

(4)slave node做複製分時候,是不會阻塞master node的正常工作的。

(5)slave node在做複製的時候,也不會阻塞自己的操作,它會用舊的資料來提供服務;但是複製完成的時候,需要刪除舊的資料,載入新的資料,這個時候會對外暫停提供服務。

(6)slave node主要用來進行橫向擴容,做讀寫分離,擴容的slave node 可以提高吞吐量。

 

3.master持久化對主從架構的安全意義

如果採用這種主從架構,那麼必須要開啟master node的持久化。不建議使用slave node作為master node的熱備份,因為如果這樣的話,如果master一旦當機,那麼master的資料就會丟失,重啟之後資料是空的,其他的slave node要是來複制資料的話,就會複製到空,這樣所有節點的資料就都丟了。

要對備份檔案做多種冷備份,防止整個機器壞了,備份的rdb資料也丟失的情況。

 

三.redis主從複製原理、斷點續傳、無磁碟化複製、過期key處理

 

1.主從複製原理

①當啟動一個slave node的時候,它會傳送一個PSYNC 命令給master node。

②如果這個slave node是重新連線master node,那麼master node 僅僅會複製給slave部分缺失的資料;如果是第一次連線master node,那麼就會觸發一次 full resynchronization。

③開始 full resynchronization的時候,master會啟動一個後臺執行緒 ,開始生成一份RDB快照檔案,同時還會將從客戶端新接收到的所有寫命令快取在記憶體當中。

④master node將生成的RDB檔案傳送給slave node,slave現將其寫入本地磁碟,然後再從磁碟載入到記憶體當中。然後master node會將記憶體中快取的寫命令傳送給slave node,slave node也會同步這部分資料 。

⑤slave node如果跟master node因為網路故障斷開了連線,會自動重連 。

⑥master如果發現有多個slave node來重新連線,僅僅會啟動一個rdb save操作 ,用一份資料服務所有slave node。

 

2.主從複製的斷點續傳

從redis2.8開始支援斷點續傳。如果在主從複製的過程中,網路突然斷掉了,那麼可以接著上次複製的地方,繼續複製,而不是從頭複製一份。

原理:

master node會在記憶體中建立一個backlog,master和slave都會儲存一個replica offset還有一個master id,offset就儲存在backlog中。如果master和slave網路連線斷掉了,slave會讓master從上次的replica offset開始繼續複製。但是如果沒有找到offset,就會執行一次 full resynchronization操作。

 

3.無磁碟化複製

無磁碟化複製是指,master直接再記憶體中建立RDB檔案,然後傳送給slave,不會在自己本地磁碟儲存資料。

設定方式

配置 repl-diskless-sync和repl-diskless-sync-delay引數。

repl-diskless-sync:該引數保證進行無磁碟化複製。

repl-diskless-sync-delay:該參數列示等待一定時長再開始複製,這樣可以等待多個slave節點從新連線上來。

 

4.過期key處理

slave不會過期key ,只有等待master過期key。

如果master過期了一個key,或者淘汰了一個key,那麼master會模擬傳送一條del命令 給slave,slave接到之後會刪除該key。

 

四.redis replication的完整流執行程和原理的再次深入剖析

 

1.複製的完整流程

①slave node啟動,僅僅儲存了master node的資訊,包括master node的host和ip,但是資料複製還沒有開始。 master node的host和ip是在redis.conf檔案裡面的slaveOf中配置的 。

②slave node內部有一個定時任務,每秒檢查是否有新的master node要連線個複製,如果發現,就跟master node建立socket網路連線。

③slave node傳送ping命令給master node。

④如果master設定了requirepass,那麼slave node必須傳送master auth的口令過去進行口令驗證。

⑤master node第一次執行全量複製,將所有資料傳送給slave node。

⑥master node持續降寫命令,非同步複製給slave node。

 

2.資料同步的機制

指的是slave第一次連線master時的情況,執行的是全量複製。

①master和slave都會維護一個offset

master會在自身不斷累加offset,slave也會在自身不斷累加offset。slave每秒都會上報自己的offset給master,同時master也會儲存每個slave的offset。

這個倒不是說特定就用在全量複製的,主要是master和slave都要知道各自的資料的offset,才能知道互相之間的資料不一致的情況

②backlog

master node有一個backlog在記憶體中,預設是1M大。

master node給slave node複製資料時,也會將資料在backlog中同步一份。

backlog主要是用來做全量複製中斷時候的增量複製的。

③master run id

redis通過info server 可以檢視到master run id。

用途:slave根據其來定位唯一的master。

為什麼不用host+ip : 因為使用host+ip來定位master是不靠譜的,如果master node重啟或者資料出現了變化,那麼slave應該根據不同的master run id進行區分,run id不同就需要做一次全量複製。

如果需要不更改run id重啟redis,可以使用redis-cli debug reload 命令。

 

3.全量複製流程與機制

①master執行bgsave,在本地生成一份RDB檔案。

②master node將RDB快照檔案傳送給slave node,如果RDB檔案的複製時間超過60秒(repl-timeout),那麼slave node就會任務複製失敗,可以適當調整這個引數。

③對於千兆網路卡的機器,一般每秒傳輸100M,傳輸6G檔案很可能超過60秒。

④master node在生成RDB檔案時,會將所有新接到的寫命令快取在記憶體中,在slave node儲存了RDB檔案之後,再將這些寫命令複製個slave node。

⑤檢視client-output-buffer-limit slave 引數,比如[client-output-buffer-limit slave 256MB 64MB 60],表示在複製期間,記憶體快取去持續消耗超過64M,或者一次性超過256MB,那麼停止複製,複製失敗。

⑥slave node接收到RDB檔案之後,清空自己的資料,然後重新載入RDB檔案到自己的記憶體中,在這個過程中,基於舊資料對外提供服務。

⑦如果slave node開啟了AOF,那麼會立即執行BRREWRITEAOF,重新AOF、rdb生成、rdb通過網路拷貝、slave舊資料的清理、slave aof rewrite,很耗費時間,如果複製的資料量在4G~6G之間,那麼很可能全量複製時間消耗到1分半到2分鐘。

 

4.增量複製流程與機制

①如果全量複製過程中,master和slave網路連線斷掉,那麼slave重新連線master會觸發增刊複製。

②master直接從自己的backlog中獲取部分丟失是資料,傳送給slave node。

③master就是根據slave傳送的psync中的offset來從backlog中獲取資料的。

 

5.心跳

master和slave互相都會傳送heartbeat資訊。

master預設每隔10秒傳送一次,slave node預設每隔1秒傳送一次。

 

6.非同步複製

master每次接收到寫命令之後,現在內部寫入資料,然後非同步傳送給slave node

 

五.redis主從架構下如何才能做到99.99%的高可用性?

 

1.什麼是99.99%高可用?

高可用性(英語:high availability,縮寫為 HA),IT術語,指系統無中斷地執行其功能的能力,代表系統的可用性程度。是進行系統設計時的準則之一。高可用性系統與構成該系統的各個元件相比可以更長時間執行。

高可用性通常通過提高系統的容錯能力來實現。定義一個系統怎樣才算具有高可用性往往需要根據每一個案例的具體情況來具體分析。

其度量方式,是根據系統損害、無法使用的時間,以及由無法運作恢復到可運作狀況的時間,與系統總運作時間的比較。計算公式為: 

A(可用性),MTBF(平均故障間隔),MDT(平均修復時間)

線上系統和執行關鍵任務的系統通常要求其可用性要達到5個9標準(99.999%)。

 

可用性年故障時間
99.9999% 32秒
99.999% 5分15秒
99.99% 52分34秒
99.9% 8小時46分
99% 3天15小時36分


2.redis不可用

redis不可以包含了單例項的不可用,主從架構的不可用。

不可用的情況:

①主從架構的master節點掛了,如果master節點掛了那麼快取資料無法再寫入,而且slave裡面的資料也無法過期,這樣就導致了不可用。

②如果是單例項,那麼可能因為其他原因導致redis程式死了。或者部署redis的機器壞了。

不可用的後果 :首先快取不可用了,那麼請求就會直接走資料庫,如果湧入大量請求超過了資料庫的承載能力,那麼資料庫就掛掉了,這時候如果不能及時處理好快取問題,那麼由於請求過多,資料庫重啟之後很快就又會掛掉,直接導致整個系統不可用。

 

3.如何實現高可用

①保證每個redis都有備份。

②保證在當前redis出故障之後,可以很快切換到備份redis上面去。

為了解決這個問題,引入下面的哨兵機制。

 

六.redis哨兵架構的相關基礎知識的講解

 

1.什麼是哨兵?

哨兵(Sentinal)是redis叢集架構當中非常重要的一個元件,它主要有一下功能:

叢集監控 ,負責監控redis master和slave程式是否正常工作。

訊息通知,如果某個redis例項有故障,那麼哨兵負責傳送訊息作為報警通知給管理員。

故障轉移,如果master掛掉了,會自動轉移到slave上。

配置中心,如果故障發生了,通知client客戶端連線到新的master上面去。

 

2.哨兵的核心知識

①哨兵本身是分散式的,需要作為一個叢集去執行,個哨兵協同工作。

②故障轉移時,判斷一個master當機了,需要大部分哨兵同意才行。

③即使部分哨兵掛掉了,哨兵叢集還是能正常工作的。

④哨兵至少需要3個例項,來保證自己的健壯性。

⑤哨兵+redis主從結構,是無法保證資料零丟失的,只會保證redis叢集的高可用。

⑥對應哨兵+redis主從這種架構,再使用之前,要做重複的測試和演練。

 

3.為什麼哨兵叢集部署2個節點無法正常工作?

哨兵叢集必須部署2個以上的節點。如果叢集僅僅部署了2個哨兵例項,那麼quorum=1(執行故障轉移需要同意的哨兵個數)。

如圖,如果這時候master1當機了,哨兵1和哨兵2中只要有一個認為master1當機了就可以進行故障轉移,同時哨兵1和哨兵2會選舉出一個哨兵來執行故障轉移。

同時這個時候需要majority(也就是所有叢集中超過一半哨兵的數量),2個哨兵那麼majority就是2,也就說需要至少2個哨兵還執行著,才可以進行故障轉移。

但是如果整個master和哨兵1同時當機了,那麼就只剩一個哨兵了,這個時候就沒有majority來執行執行故障轉移了,雖然兩外一臺機器還有一個哨兵,但是1無法大於1,也就是無法保證半數以上,因此故障轉移不會執行。

 

4.經典的3節點哨兵叢集

Configuration: quorum = 2,majority=2

如果M1所在機器當機了,那麼三個哨兵還剩下2個,S2和S3可以一致認為master當機,然後選舉出一個來執行故障轉移

同時3個哨兵的majority是2,所以還剩下的2個哨兵執行著,就可以允許執行故障轉移

 

七.redis哨兵主備切換的資料丟失問題:非同步複製、叢集腦裂

 

1.兩種資料丟失的場景

①非同步複製導致的資料丟失

因為從master到slave的資料複製過程是非同步的,可能有部分資料還沒來得及複製到slave上面去,這時候master就當機了,那麼這部分資料就丟失了。

②叢集腦裂導致的資料丟失

什麼是腦裂:腦裂,也就是說,某個master所在機器突然脫離了正常的網路,跟其他slave機器不能連線,但是實際上master還執行著。

此時哨兵可能就會認為master當機了,然後開啟選舉,將其他slave切換成了master。

這個時候,叢集裡就會有兩個master,也就是所謂的腦裂。

此時雖然某個slave被切換成了master,但是可能client還沒來得及切換到新的master,還繼續寫向舊master的資料可能也丟失了。

因此舊master再次恢復的時候,會被作為一個slave掛到新的master上去,自己的資料會清空,重新從新的master複製資料

 

2.解決非同步複製的腦裂導致的資料丟失

要解決這個問題,就需要配置兩個引數:

min-slaves-to-write 1 和 min-slaves-max-lag :

表示 要求至少有一個slave 在進行資料的複製和同步的延遲不能超過10秒。

如果一旦所有的slave資料同步和複製的延遲都超過了10秒,那麼這個時候,master就會在接受任何請求了。

①減少非同步複製的資料丟失

有了min-slaves-max-lag這個配置,就可以確保說,一旦slave複製資料和ack延時太長,就認為可能master當機後損失的資料太多了,那麼就拒絕寫請求,這樣可以把master當機時由於部分資料未同步到slave導致的資料丟失降低的可控範圍內。

②減少腦裂的資料丟失

如果一個master出現了腦裂,跟其他slave丟了連線,那麼上面兩個配置可以確保說,如果不能繼續給指定數量的slave傳送資料,而且slave超過10秒沒有給自己ack訊息,那麼就直接拒絕客戶端的寫請求。

這樣腦裂後的舊master就不會接受client的新資料,也就避免了資料丟失。

上面的配置就確保了,如果跟任何一個slave丟了連線,在10秒後發現沒有slave給自己ack,那麼就拒絕新的寫請求。

因此在腦裂場景下,最多就丟失10秒的資料

 

八.redis哨兵的多個核心底層原理的深入解析(包含slave選舉演算法)

 

1.sdown和odown兩種狀態

sdown是主觀當機,就一個哨兵如果自己覺得一個master當機了,那麼就是主觀當機。

odown是客觀當機,如果quorum數量的哨兵都覺得一個master當機了,那麼就是客觀當機

sdown達成的條件很簡單,如果一個哨兵ping一個master,超過了is-master-down-after-milliseconds指定的毫秒數之後,就主觀認為master當機。

sdown到odown轉換的條件很簡單,如果一個哨兵在指定時間內,收到了quorum指定數量的其他哨兵也認為那個master是sdown了,那麼就認為是odown了,客觀認為master當機。

 

2.哨兵叢集的欄位發現機制

①哨兵相互之間的發現,是通過redis的pub/sub系統實現的,每個哨兵都會往 __sentinel__:hello 這個channel裡面傳送一個訊息,這時候其他的哨兵都可以消費這個訊息,並感知其他哨兵的存在。

②每個兩秒鐘,每個哨兵都會往自己監控的某個master+slave對應的 __sentinel__:hello channel裡面傳送一個訊息,內容是自己的host、ip和run id還有對這個master的監控配置。

③每個哨兵也會去監聽自己監控的每個master+slave對應的 __sentinel__:hello channel,r然後去感知到同樣在監聽這個master+slave的其他哨兵的存在。

④每個哨兵還會根據其他哨兵交換對master的監控配置,互相進行監控配置的同步。

 

3.slave配置的自我糾正

哨兵會負責自動糾正slave的一些配置,比如slave如果要成為潛在的master候選人,哨兵會確保slave在複製現有master資料;如果slave連線到了一個錯誤的master上,比如故障轉移之後,那麼哨兵會確保他們連線到正確的master上來。

 

4.選舉演算法

如果一個master被認為odown了,而且majority數量的哨兵都允許了主備切換,那麼某個哨兵就會執行主備切換,此時首先要選舉一個slave出來。選舉會考慮到一下情況:

①slave跟master斷開連線的時長

②slave的優先順序

③slave複製資料的offset

④slave的run id

 

首先,如果一個slave跟master斷開連線已經超過了 down-after-millisecondes 的10倍,外加master當機的時長,那麼slave就被認為不適合選舉為master了。

即:斷開連線時間 > (down-after-milliseconds * 10 + milliseconds_since_master_is_in_SDOWN_state).

 

對應剩下的slave按照如下規定排序:

①首先,按照slave的優先順序進行排序,slave priority越低,優先順序就越高。

②如果優先順序相同,那麼就看replica offset,那個slave複製了越多的資料,offset越靠後,優先順序就越高。

③如果上面都想同,那就選擇run id最小的那個slave。

 

5.quorum和majority

每次一個哨兵做主備切換,首先需要quorum數量的哨兵認為odown,然後選舉出一個哨兵來做主備切換,這個哨兵還要得到majority數量哨兵的授權,才能正式執行切換。

 

如果quorum < majority ,比如5個哨兵,majority就是3(超過半數),quorum設定為2,那麼就需要3個哨兵授權就可以執行切換。

 

如果 quorum >= majority,那麼必須quorum數量的哨兵都授權才可以進行切換,比如5個哨兵,quorum是5,那麼必須5個哨兵都同意授權,才可以進行切換。

 

6.configuration epoch

哨兵會對一套redis master+slave進行監控,有相應的監控的配置。

 

執行切換的那個哨兵,會從要切換到的新master(salve->master)那裡得到一個configuration epoch,這就是一個version號,每次切換的version號都必須是唯一的。

 

如果第一個選舉出的哨兵切換失敗了,那麼其他哨兵,會等待failover-timeout時間,然後接替繼續執行切換,此時會重新獲取一個新的configuration epoch,作為新的version號。

 

7、configuraiton傳播

哨兵完成切換之後,會在自己本地更新生成最新的master配置,然後同步給其他的哨兵,就是通過之前說的pub/sub訊息機制。

 

這裡之前的version號就很重要了,因為各種訊息都是通過一個channel去釋出和監聽的,所以一個哨兵完成一次新的切換之後,新的master配置是跟著新的version號的。

 

其他的哨兵都是根據版本號的大小來更新自己的master配置的。

相關文章