redis 實現高并發主要依靠主從架構,一主多從.
對于性能來說,單主用來寫入數據,單機幾萬QPS,多從用來查詢數據,多個從實例可以提供每秒 10w 的 QPS。
如果想要在實現高并發的同時,容納大量的數據,那么就需要 redis 集群,
使用 redis cluster 模式,可以提供每秒幾十萬的讀寫并發。
redis 高可用,如果是做主從架構部署,那么加上哨兵就可以了,就可以實現,任何一個實例宕機,可以進行主備切換。
所以就有了幾個問題?
什么是主從架構,主從如何備份?
什么是redis cluster模式?
什么是哨兵集群?
redis 主從架構
主從(master-slave)架構,一主多從,主負責寫,并且將數據復制到其它的 slave 節點,從節點負責讀。所有的讀請求全部走從節點。這樣也可以很輕松實現水平擴容,支撐讀高并發。
Redis雖然讀取寫入的速度都特別快,但是也會產生讀壓力特別大的情況,所以為了緩解讀的壓力,所以進行讀寫分類,并對讀進行擴展。
優點:
1、解決數據備份問題
2、做到讀寫分離,提高服務器性能
缺點:
1、每個客戶端連接redis實例的時候都是指定了ip和端口號的,如果所連接的redis實例因為故障下線了,而主從模式也沒有提供一定的手段通知客戶端另外可連接的客戶端地址,因而需要手動更改客戶端配置重新連接
2、主從模式下,如果主節點由于故障下線了,那么從節點因為沒有主節點而同步中斷,因而需要人工進行故障轉移工作
3、無法實現動態擴容
主從架構就涉及到一個數據從主節點同步到從節點的問題。涉及redis replication問題
redis replication 的核心機制
當啟動一個 slave node 的時候,它會發送一個 PSYNC 命令給 master node,如果這是第一次連接master node 那么會觸發一次 full resynchronization 全量復制.全量復制的時候,master 會啟動一個后臺線程,開始生成一份 RDB 快照文件,同時還會將從客戶端新收到的所有寫命令緩存在內存中。最后將生成的RDB文件發送給slave,slave會先寫入本地磁盤,然后再從本地磁盤加載到內存中,接著 master 會將內存中緩存的寫命令發送到 slave,slave 也會同步這些數據。
如果是連接之后 master node 僅會復制給 slave 部分缺少的數據。master 如果發現有多個slave node都來重新連接,僅僅會啟動一個rdb save操作,用一份數據服務所有slave node。
注:redis2.8 開始,就支持主從復制的斷點續傳,如果主從復制過程中,斷掉了,那么可以接著上次復制的地方,繼續復制下去,而不是從頭開始復制一份。
master node 會在內存中維護一個 backlog,master 和 slave 都會保存一個 replica offset 還有一個 master run id,offset 就是保存在 backlog 中的。如果 master 和 slave 網絡連接斷掉了,slave 會讓 master 從上次 replica offset 開始繼續復制,如果沒有找到對應的 offset,那么就會執行一次 resynchronization。
redis保證數據同步機制
- master和slave都會維護一個offset
master會在自身不斷累加offset,slave也會在自身不斷累加offset
slave每秒都會上報自己的offset給master,同時master也會保存每個slave的offset,檢測offset來保證數據的一致性
- backlog
master node有一個backlog,默認是1MB大小
master node給slave node復制數據時,也會將數據在backlog中同步寫一份
backlog主要是用來做全量復制中斷候的增量復制的
- master run id
如果根據host+ip定位master node,是不靠譜的,如果master node重啟或者數據出現了變化,那么slave node應該根據不同的run id區分,run id不同就做全量復制
如果需要不更改run id重啟redis,可以使用redis-cli debug reload命令
- psync
從節點使用psync從master node進行復制,psync runid offset。master node會根據自身的情況返回響應信息,可能是FULLRESYNC runid offset觸發全量復制,可能是CONTINUE觸發增量復制
redis cluster 模式
redis 服務節點中任何兩個節點之間都是相互連通的。客戶端可以與任何一個節點相連接,然后就可以訪問集群中的任何一個節點。對其進行存取和其他操作。
一般集群建議搭建三主三從架構,三主提供服務,三從提供備份功能。
每一個節點都存有這個集群所有主節點以及從節點的信息。
Redis集群數據分片
在redis的每一個節點上,都有這么兩個東西,一個是插槽(slot)可以理解為是一個可以存儲兩個數值的一個變量這個變量的取值范圍是:0-16383。還有一個就是cluster我個人把這個cluster理解為是一個集群管理的插件。當我們的存取的key到達的時候,redis會根據crc16的算法得出一個結果,然后把結果對 16384 求余數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,通過這個值,去找到對應的插槽所對應的節點,然后直接自動跳轉到這個對應的節點上進行存取操作。
投票過程是集群中所有master參與,如果半數以上master節點與master節點通信超時(cluster-node-timeout),認為當前master節點掛掉.
什么時候整個集群不可用(cluster_state:fail)?
如果集群任意master掛掉,且當前master沒有slave.集群進入fail狀態,也可以理解成集群的slot映射[0-16383]不完整時進入fail狀態.
一個Redis實例具備了“數據存儲”和“路由重定向”,完全去中心化的設計。這帶來的好處是部署非常簡單,直接部署Redis就行,不像Codis有那么多的組件和依賴。但帶來的問題是很難對業務進行無痛的升級,如果哪天Redis集群出了什么嚴重的Bug,就只能回滾整個Redis集群。
優點:
1、有效的解決了redis在分布式方面的需求
2、遇到單機內存,并發和流量瓶頸等問題時,可采用Cluster方案達到負載均衡的目的
3、可實現動態擴容
4、P2P模式,無中心化
5、通過Gossip協議同步節點信息
6、自動故障轉移、Slot遷移中數據可用
缺點:
1、架構比較新,最佳實踐較少
2、為了性能提升,客戶端需要緩存路由表信息
3、節點發現、reshard操作不夠自動化
redis 哨兵
哨兵的功能:
集群監控:負責監控 redis master 和 slave 進程是否正常工作。
消息通知:如果某個 redis 實例有故障,那么哨兵負責發送消息作為報警通知給管理員。
故障轉移:如果 master node 掛掉了,會自動轉移到 slave node 上。
配置中心:如果故障轉移發生了,通知 client 客戶端新的 master 地址。
哨兵用于實現 redis 集群的高可用,本身也是分布式的,作為一個哨兵集群去運行,互相協同工作。
故障轉移時,判斷一個 master node 是否宕機了,需要大部分的哨兵都同意才行,涉及到了分布式選舉的問題。
即使部分哨兵節點掛掉了,哨兵集群還是能正常工作的。
哨兵的核心知識
哨兵至少需要 3 個實例,來保證自己的健壯性。
哨兵 + redis 主從的部署架構,是不保證數據零丟失的,只能保證 redis 集群的高可用性。
對于哨兵 + redis 主從這種復雜的部署架構,盡量在測試環境和生產環境,都進行充足的測試和演練。
數據丟失問題的解決方案?
進行如下配置:
min-slaves-to-write 1
min-slaves-max-lag 10
要求至少有 1 個 slave,數據復制和同步的延遲不能超過 10 秒。如果說一旦所有的 slave,數據復制和同步的延遲都超過了 10 秒鐘,那么這個時候,master 就不會再接收任何請求了。
減少異步復制數據的丟失
有了 min-slaves-max-lag 這個配置,就可以確保說,一旦 slave 復制數據和 ack 延時太長,就認為可能 master 宕機后損失的數據太多了,那么就拒絕寫請求,這樣可以把 master 宕機時由于部分數據未同步到 slave 導致的數據丟失降低的可控范圍內。
減少腦裂的數據丟失
如果一個 master 出現了腦裂,跟其他 slave 丟了連接,那么上面兩個配置可以確保說,如果不能繼續給指定數量的 slave 發送數據,而且 slave 超過 10 秒沒有給自己 ack 消息,那么就直接拒絕客戶端的寫請求。
怎么保證redis是高并發以及高可用的?
- 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 的一些配置,比如 slave 如果要成為潛在的 master 候選人,哨兵會確保 slave 復制現有 master 的數據; 如果 slave 連接到了一個錯誤的 master 上,比如故障轉移之后,那么哨兵會確保它們連接到正確的 master 上。
- slave->master 選舉算法
如果一個 master 被認為 odown 了,而且 majority 數量的哨兵都允許主備切換,那么某個哨兵就會執行主備切換操作,此時首先要選舉一個 slave 來,會考慮 slave 的一些信息:
跟 master 斷開連接的時長
0slave 優先級
復制 offset
run id
如果一個 slave 跟 master 斷開連接的時間已經超過了down-after-milliseconds的 10 倍,外加 master 宕機的時長,那么 slave 就被認為不適合選舉為 master。(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下來會對 slave 進行排序:
按照 slave 優先級進行排序,slave priority 越低,優先級就越高。
如果 slave priority 相同,那么看 replica offset,哪個 slave 復制了越多的數據,offset 越靠后,優先級就越高。
如果上面兩個條件都相同,那么選擇一個 run id 比較小的那個 slave。
- quorum 和 majority
每次一個哨兵要做主備切換,首先需要 quorum 數量的哨兵認為 odown,然后選舉出一個哨兵來做切換,這個哨兵還得得到 majority 哨兵的授權,才能正式執行切換。
??如果 quorum < majority,比如 5 個哨兵,majority 就是 3,quorum 設置為2,那么就 3 個哨兵授權就可以執行切換。
??但是如果 quorum >= majority,那么必須 quorum 數量的哨兵都授權,比如 5 個哨兵,quorum 是 5,那么必須 5 個哨兵都同意授權,才能執行切換。
- configuration epoch
哨兵會對一套 redis master+slaves 進行監控,有相應的監控的配置。
??執行切換的那個哨兵,會從要切換到的新 master(salve->master)那里得到一個 configuration epoch,這就是一個 version 號,每次切換的 version 號都必須是唯一的。
??如果第一個選舉出的哨兵切換失敗了,那么其他哨兵,會等待 failover-timeout 時間,然后接替繼續執行切換,此時會重新獲取一個新的 configuration epoch,作為新的 version 號。
- configuraiton 傳播
哨兵完成切換之后,會在自己本地更新生成最新的 master 配置,然后同步給其他的哨兵,就是通過之前說的 pub/sub 消息機制。
??這里之前的 version 號就很重要了,因為各種消息都是通過一個 channel 去發布和監聽的,所以一個哨兵完成一次新的切換之后,新的 master 配置是跟著新的 version 號的。其他的哨兵都是根據版本號的大小來更新自己的 master 配置的。