怎麼保證Redis是高併發以及高可用的?從石杉碼農課程整理而來node
redis,你要搞高併發的話,不可避免,要把底層的緩存搞得很好mysql
mysql,高併發,作到了,那麼也是經過一系列複雜的分庫分表,訂單系統,事務要求的,QPS到幾萬,比較高了redis
要作一些電商的商品詳情頁,真正的超高併發,QPS上十萬,甚至是百萬,一秒鐘百萬的請求量算法
光是redis是不夠的,可是redis是整個大型的緩存架構中,支撐高併發的架構裏面,很是重要的一個環節sql
首先,你的底層的緩存中間件,緩存系統,必須可以支撐的起咱們說的那種高併發,其次,再通過良好的總體的緩存架構的設計(多級緩存架構、熱點緩存),支撐真正的上十萬,甚至上百萬的高併發緩存
單機安全
單機的redis幾乎不太可能說QPS超過10萬+,除非一些特殊狀況,好比你的機器性能特別好,配置特別高,物理機,維護作的特別好,並且你的總體的操做不是太複雜bash
單機在幾萬網絡
讀寫分離,通常來講,對緩存,通常都是用來支撐讀高併發的,寫的請求是比較少的,可能寫請求也就一秒鐘幾千,一兩千架構
大量的請求都是讀,一秒鐘二十萬次讀
讀寫分離
主從架構 -> 讀寫分離 -> 支撐10萬+讀QPS的架構
課程大綱
一、圖解redis replication基本原理 二、redis replication的核心機制 三、master持久化對於主從架構的安全保障的意義
redis replication -> 主從架構 -> 讀寫分離 -> 水平擴容支撐讀高併發
redis replication的最最基本的原理,鋪墊
(1)redis採用異步方式複製數據到slave節點,不過redis 2.8開始,slave node會週期性地確認本身每次複製的數據量 (2)一個master node是能夠配置多個slave node的 (3)slave node也能夠鏈接其餘的slave node (4)slave node作複製的時候,是不會block master node的正常工做的 (5)slave node在作複製的時候,也不會block對本身的查詢操做,它會用舊的數據集來提供服務; 可是複製完成的時候,須要刪除舊數據集,加載新數據集,這個時候就會暫停對外服務了 (6)slave node主要用來進行橫向擴容,作讀寫分離,擴容的slave node能夠提升讀的吞吐量
slave,高可用性,有很大的關係
若是採用了主從架構,那麼建議必須開啓master node的持久化!
不建議用slave node做爲master node的數據熱備,由於那樣的話,若是你關掉master的持久化,可能在master宕機重啓的時候數據是空的,而後可能一通過複製,slave node數據也丟了
master -> RDB和AOF都關閉了 -> 所有在內存中
master宕機,重啓,是沒有本地數據能夠恢復的,而後就會直接認爲本身的數據是空的
master就會將空的數據集同步到slave上去,全部slave的數據所有清空
100%的數據丟失
即便採用了後續講解的高可用機制,slave node能夠自動接管master node,可是也可能sentinal尚未檢測到master failure,master node就自動重啓了,仍是可能致使上面的全部slave node數據清空故障
當啓動一個slave node的時候,它會發送一個PSYNC命令給master node
若是這是slave node從新鏈接master node,那麼master node僅僅會複製給slave部分缺乏的數據; 不然若是是slave node第一次鏈接master node,那麼會觸發一次full resynchronization
開始full resynchronization的時候,master會啓動一個後臺線程,開始生成一份RDB快照文件,同時還會將從客戶端收到的全部寫命令緩存在內存中。RDB文件生成完畢以後,master會將這個RDB發送給slave,slave會先寫入本地磁盤,而後再從本地磁盤加載到內存中。而後master會將內存中緩存的寫命令發送給slave,slave也會同步這些數據。
slave node若是跟master node有網絡故障,斷開了鏈接,會自動重連。master若是發現有多個slave node都來從新鏈接,僅僅會啓動一個rdb save操做,用一份數據服務全部slave node。
從redis 2.8開始,就支持主從複製的斷點續傳,若是主從複製過程當中,網絡鏈接斷掉了,那麼能夠接着上次複製的地方,繼續複製下去,而不是從頭開始複製一份
master node會在內存中常見一個backlog,master和slave都會保存一個replica offset還有一個master id,offset就是保存在backlog中的。若是master和slave網絡鏈接斷掉了,slave會讓master從上次的replica offset開始繼續複製
可是若是沒有找到對應的offset,那麼就會執行一次resynchronization
master在內存中直接建立rdb,而後發送給slave,不會在本身本地落地磁盤了
repl-diskless-sync repl-diskless-sync-delay,等待必定時長再開始複製,由於要等更多slave從新鏈接過來
slave不會過時key,只會等待master過時key。若是master過時了一個key,或者經過LRU淘汰了一個key,那麼會模擬一條del命令發送給slave。
(1)slave node啓動,僅僅保存master node的信息,包括master node的host和ip,可是複製流程沒開始
master host和ip是從哪兒來的,redis.conf裏面的slaveof配置的
(2)slave node內部有個定時任務,每秒檢查是否有新的master node要鏈接和複製,若是發現,就跟master node創建socket網絡鏈接 (3)slave node發送ping命令給master node (4)口令認證,若是master設置了requirepass,那麼slave node必須發送masterauth的口令過去進行認證 (5)master node第一次執行全量複製,將全部數據發給slave node (6)master node後續持續將寫命令,異步複製給slave node
指的就是第一次slave鏈接msater的時候,執行的全量複製,那個過程裏面你的一些細節的機制
(1)master和slave都會維護一個offset
master會在自身不斷累加offset,slave也會在自身不斷累加offset slave每秒都會上報本身的offset給master,同時master也會保存每一個slave的offset
這個倒不是說特定就用在全量複製的,主要是master和slave都要知道各自的數據的offset,才能知道互相之間的數據不一致的狀況
(2)backlog
master node有一個backlog,默認是1MB大小 master node給slave node複製數據時,也會將數據在backlog中同步寫一份 backlog主要是用來作全量複製中斷後的增量複製的
(3)master run id
info server,能夠看到master run id 若是根據host+ip定位master node,是不靠譜的,若是master node重啓或者數據出現了變化,那麼slave node應該根據不一樣的run id區分,run id不一樣就作全量複製 若是須要不更改run id重啓redis,可使用redis-cli debug reload命令
(4)psync
從節點使用psync從master node進行復制,psync runid offset master node會根據自身的狀況返回響應信息,多是FULLRESYNC runid offset觸發全量複製,多是CONTINUE觸發增量複製
(1)master執行bgsave,在本地生成一份rdb快照文件 (2)master node將rdb快照文件發送給slave node,若是rdb複製時間超過60秒(repl-timeout),那麼slave node就會認爲複製失敗,能夠適當調節大這個參數 (3)對於千兆網卡的機器,通常每秒傳輸100MB,6G文件,極可能超過60s (4)master node在生成rdb時,會將全部新的寫命令緩存在內存中,在slave node保存了rdb以後,再將新的寫命令複製給slave node (5)client-output-buffer-limit slave 256MB 64MB 60,若是在複製期間,內存緩衝區持續消耗超過64MB,或者一次性超過256MB,那麼中止複製,複製失敗 (6)slave node接收到rdb以後,清空本身的舊數據,而後從新加載rdb到本身的內存中,同時基於舊的數據版本對外提供服務 (7)若是slave node開啓了AOF,那麼會當即執行BGREWRITEAOF,重寫AOF
rdb生成、rdb經過網絡拷貝、slave舊數據的清理、slave aof rewrite,很耗費時間
若是複製的數據量在4G~6G之間,那麼極可能全量複製時間消耗到1分半到2分鐘
(1)若是全量複製過程當中,master-slave網絡鏈接斷掉,那麼slave從新鏈接master時,會觸發增量複製 (2)master直接從本身的backlog中獲取部分丟失的數據,發送給slave node,默認backlog就是1MB (3)msater就是根據slave發送的psync中的offset來從backlog中獲取數據的
主從節點互相都會發送heartbeat信息
master默認每隔10秒發送一次heartbeat,slave node每隔1秒發送一個heartbeat
master每次接收到寫命令以後,如今內部寫入數據,而後異步發送給slave node
架構上,高可用性,99.99%的高可用性
講的學術,99.99%,公式,系統可用的時間 / 系統故障的時間,365天,在365天 * 99.99%的時間內,你的系統都是能夠嘩嘩對外提供服務的,那就是高可用性,99.99%
系統可用的時間 / 總的時間 = 高可用性,而後會對各類時間的概念,說一大堆解釋
redis不可用是什麼
單實例不可用?主從架構不可用?
不可用的後果是什麼?
高併發高性能的緩存不可用了,超過mysql最大承載能力大併發的大流量會涌入mysql中,致使mysql宕機
redis的高可用架構,叫作故障轉移 failover,也能夠叫作主備切換。
在master node故障時,自動檢測,而且將某個slave node自動切換爲master node的過程,叫作主備切換。這個過程,實現了redis的主從架構下的高可用性。
一旦master故障,在很短的時間內,就會切換到另外一個master上去,可能就幾分鐘、幾秒鐘redis是不可用的。
這都依賴於sentinal node,即哨兵。
sentinal,中文名是哨兵
哨兵是redis集羣架構中很是重要的一個組件,主要功能以下
(1)集羣監控,負責監控redis master和slave進程是否正常工做 (2)消息通知,若是某個redis實例有故障,那麼哨兵負責發送消息做爲報警通知給管理員 (3)故障轉移,若是master node掛掉了,會自動轉移到slave node上 (4)配置中心,若是故障轉移發生了,通知client客戶端新的master地址
哨兵自己也是分佈式的,做爲一個哨兵集羣去運行,互相協同工做
(1)故障轉移時,判斷一個master node是宕機了,須要大部分的哨兵都贊成才行,涉及到了分佈式選舉的問題 (2)即便部分哨兵節點掛掉了,哨兵集羣仍是能正常工做的,由於若是一個做爲高可用機制重要組成部分的故障轉移系統自己是單點的,那就很坑爹了
目前採用的是sentinal 2版本,sentinal 2相對於sentinal 1來講,重寫了不少代碼,主要是讓故障轉移的機制和算法變得更加健壯和簡單
(1)哨兵至少須要3個實例,來保證本身的健壯性 (2)哨兵 + redis主從的部署架構,是不會保證數據零丟失的,只能保證redis集羣的高可用性 (3)對於哨兵 + redis主從這種複雜的部署架構,儘可能在測試環境和生產環境,都進行充足的測試和演練
哨兵集羣必須部署2個以上節點
若是哨兵集羣僅僅部署了個2個哨兵實例,quorum=1
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
複製代碼
Configuration: quorum = 1
master宕機,s1和s2中只要有1個哨兵認爲master宕機就能夠還行切換,同時s1和s2中會選舉出一個哨兵來執行故障轉移
同時這個時候,須要majority,也就是大多數哨兵都是運行的,2個哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2個哨兵都運行着,就能夠容許執行故障轉移
可是若是整個M1和S1運行的機器宕機了,那麼哨兵只有1個了,此時就沒有majority來容許執行故障轉移,雖然另一臺機器還有一個R1,可是故障轉移不會執行
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
複製代碼
Configuration: quorum = 2,majority
若是M1所在機器宕機了,那麼三個哨兵還剩下2個,S2和S3能夠一致認爲master宕機,而後選舉出一個來執行故障轉移
同時3個哨兵的majority是2,因此還剩下的2個哨兵運行着,就能夠容許執行故障轉移
課程大綱
一、兩種數據丟失的狀況 二、解決異步複製和腦裂致使的數據丟失
主備切換的過程,可能會致使數據丟失
(1)異步複製致使的數據丟失
由於master -> slave的複製是異步的,因此可能有部分數據還沒複製到slave,master就宕機了,此時這些部分數據就丟失了
(2)腦裂致使的數據丟失
腦裂,也就是說,某個master所在機器忽然脫離了正常的網絡,跟其餘slave機器不能鏈接,可是實際上master還運行着
此時哨兵可能就會認爲master宕機了,而後開啓選舉,將其餘slave切換成了master
這個時候,集羣裏就會有兩個master,也就是所謂的腦裂
此時雖然某個slave被切換成了master,可是可能client還沒來得及切換到新的master,還繼續寫向舊master的數據可能也丟失了
所以舊master再次恢復的時候,會被做爲一個slave掛到新的master上去,本身的數據會清空,從新重新的master複製數據
min-slaves-to-write 1 min-slaves-max-lag 10
要求至少有1個slave,數據複製和同步的延遲不能超過10秒
若是說一旦全部的slave,數據複製和同步的延遲都超過了10秒鐘,那麼這個時候,master就不會再接收任何請求了
上面兩個配置能夠減小異步複製和腦裂致使的數據丟失
(1)減小異步複製的數據丟失
有了min-slaves-max-lag這個配置,就能夠確保說,一旦slave複製數據和ack延時太長,就認爲可能master宕機後損失的數據太多了,那麼就拒絕寫請求,這樣能夠把master宕機時因爲部分數據未同步到slave致使的數據丟失下降的可控範圍內
(2)減小腦裂的數據丟失
若是一個master出現了腦裂,跟其餘slave丟了鏈接,那麼上面兩個配置能夠確保說,若是不能繼續給指定數量的slave發送數據,並且slave超過10秒沒有給本身ack消息,那麼就直接拒絕客戶端的寫請求
這樣腦裂後的舊master就不會接受client的新數據,也就避免了數據丟失
上面的配置就確保了,若是跟任何一個slave丟了鏈接,在10秒後發現沒有slave給本身ack,那麼就拒絕新的寫請求
所以在腦裂場景下,最多就丟失10秒的數據
sdown和odown兩種失敗狀態
sdown是主觀宕機,就一個哨兵若是本身以爲一個master宕機了,那麼就是主觀宕機
odown是客觀宕機,若是quorum數量的哨兵都以爲一個master宕機了,那麼就是客觀宕機
sdown達成的條件很簡單,若是一個哨兵ping一個master,超過了is-master-down-after-milliseconds指定的毫秒數以後,就主觀認爲master宕機
sdown到odown轉換的條件很簡單,若是一個哨兵在指定時間內,收到了quorum指定數量的其餘哨兵也認爲那個master是sdown了,那麼就認爲是odown了,客觀認爲master宕機
哨兵互相之間的發現,是經過redis的pub/sub系統實現的,每一個哨兵都會往__sentinel__:hello這個channel裏發送一個消息,這時候全部其餘哨兵均可以消費到這個消息,並感知到其餘的哨兵的存在
每隔兩秒鐘,每一個哨兵都會往本身監控的某個master+slaves對應的__sentinel__:hello channel裏發送一個消息,內容是本身的host、ip和runid還有對這個master的監控配置
每一個哨兵也會去監聽本身監控的每一個master+slaves對應的__sentinel__:hello channel,而後去感知到一樣在監聽這個master+slaves的其餘哨兵的存在
每一個哨兵還會跟其餘哨兵交換對master的監控配置,互相進行監控配置的同步
哨兵會負責自動糾正slave的一些配置,好比slave若是要成爲潛在的master候選人,哨兵會確保slave在複製現有master的數據; 若是slave鏈接到了一個錯誤的master上,好比故障轉移以後,那麼哨兵會確保它們鏈接到正確的master上
若是一個master被認爲odown了,並且majority哨兵都容許了主備切換,那麼某個哨兵就會執行主備切換操做,此時首先要選舉一個slave來
會考慮slave的一些信息
(1)跟master斷開鏈接的時長 (2)slave優先級 (3)複製offset (4)run id
若是一個slave跟master斷開鏈接已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那麼slave就被認爲不適合選舉爲master
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下來會對slave進行排序
(1)按照slave優先級進行排序,slave priority越低,優先級就越高 (2)若是slave priority相同,那麼看replica offset,哪一個slave複製了越多的數據,offset越靠後,優先級就越高 (3)若是上面兩個條件都相同,那麼選擇一個run id比較小的那個slave
每次一個哨兵要作主備切換,首先須要quorum數量的哨兵認爲odown,而後選舉出一個哨兵來作切換,這個哨兵還得獲得majority哨兵的受權,才能正式執行切換
若是quorum < majority,好比5個哨兵,majority就是3,quorum設置爲2,那麼就3個哨兵受權就能夠執行切換
可是若是quorum >= majority,那麼必須quorum數量的哨兵都受權,好比5個哨兵,quorum是5,那麼必須5個哨兵都贊成受權,才能執行切換
哨兵會對一套redis master+slave進行監控,有相應的監控的配置
執行切換的那個哨兵,會從要切換到的新master(salve->master)那裏獲得一個configuration epoch,這就是一個version號,每次切換的version號都必須是惟一的
若是第一個選舉出的哨兵切換失敗了,那麼其餘哨兵,會等待failover-timeout時間,而後接替繼續執行切換,此時會從新獲取一個新的configuration epoch,做爲新的version號
哨兵完成切換以後,會在本身本地更新生成最新的master配置,而後同步給其餘的哨兵,就是經過以前說的pub/sub消息機制
這裏以前的version號就很重要了,由於各類消息都是經過一個channel去發佈和監聽的,因此一個哨兵完成一次新的切換以後,新的master配置是跟着新的version號的
其餘的哨兵都是根據版本號的大小來更新本身的master配置的
redis高併發:主從架構,一主多從,通常來講,不少項目其實就足夠了,單主用來寫入數據,單機幾萬QPS,多從用來查詢數據,多個從實例能夠提供每秒10萬的QPS。
redis高併發的同時,還須要容納大量的數據:一主多從,每一個實例都容納了完整的數據,好比redis主就10G的內存量,其實你最多隻能容納10g的數據量。若是你的緩存要容納的數據量很大,達到了幾十g,甚至幾百g,或者是幾t,那你就須要redis集羣,並且用redis集羣以後,能夠提供可能每秒幾十萬的讀寫併發。
redis高可用:若是你作主從架構部署,其實就是加上哨兵就能夠了,就能夠實現,任何一個實例宕機,自動會進行主備切換。