天天看點

關于Redis的幾件小事 | 高并發和高可用

關于Redis的幾件小事 | 高并發和高可用

如果你用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配置的。

原文位址

https://www.cnblogs.com/feixiangmanon/p/11229620.html