前言:Redis是被廣泛使用的基礎軟件之一,對於架構師和運維人員來說,瞭解redis的高可用方案和原理是必備的基礎知識。
作者簡介:codedump.info 博主,多年從事互聯網服務器後臺開發工作。可訪問作者博客 https://www.codedump.info 更多文章。
我是一個字一個字打下來的,就是爲了加深印象,這篇文章深入淺出,很多原理解釋的很透徹。以下是正文。
Redis爲了實現高可用採用瞭如下兩個方式:
- 主從複製數據
- 採用哨兵監控數據節點的運行情況,一旦主節點出現問題,則由從節點頂上繼續服務。
主從複製
Redis中複製主節點有全量複製和部分複製兩種。
舊版本全量複製的實現:
全量複製使用Sync命令來實現,其流程是:
- 從服務器向主服務器發送Sync命令。
- 主服務器在收到Sync命令之後,調用Bigsave命令生成RDB文件,將這個文件同步給從服務器,這樣的服務器載入這個RDB文件之後,狀態就會和主服務器執行Bigsave命令時一致。
- 主服務器將保存在命令緩衝區中的寫命令同步給從服務器,從服務器執行這些命令,這樣的從服務器的狀態就跟主服務器一致了。
舊版本全量複製功能,其最大的問題就是從服務器斷線重連時,即便在從服務器上已經有部分數據了,也需要進行全量複製,這樣做的效率很低,於是新版本的Redis也做了部分改進。
新版本全量複製功能的實現
新版本Redis使用Psync命令代替Sync命令,該命令既可以實現全量同步,也可以實現部分同步。
複製偏移量
執行復制的雙方,主從服務器,分別會維護一個複製偏移量。
- 主服務器每次向從服務器同步了N字節數據後,將修改自己的複製偏移量加N。
- 從服務器每次向主服務器同步了N字節數據後,將修改自己的複製偏移量加N。
複製及壓緩衝區
主服務器內部維護了一個固定長度的先進先出隊列作爲複製積壓緩衝區,其默認大小爲1M。
在主服務器進行命令傳播時,不僅會將命令同步到從服務器,還會將寫命令寫入到複製積壓緩衝區。
服務器運行ID
每個Redis服務器都有其運行ID,運行ID在服務器啓動時自動生成,主服務器會將自己的運行ID發給從服務器,而從服務器會將主服務器的運行ID保存起來。
從服務器Redis斷線重連之後進行同步時,就是根據運行ID來判斷同步的進度:
- 如果從服務器上面保存的主服務器運行ID與當前主服務器運行ID一致,則認爲這一次斷線重新連接的是之前的主服務器,主服務器可以繼續嘗試部分同步操作。
- 如果前後兩次的主服務器ID不同,則進行全量複製。
Psync命令流程
有了前面的準備,現在開始分析Psync命令的流程:
- 如果從服務器之前沒有複製過任何主服務器,或者之前執行過Slave no one命令,那麼從服務器就會向主服務發送psync ? -1命令,請求主服務器進行全量同步。
- 否則,前面從服務器已經同步過部分數據,那麼從服務器向主服務器發送psync <runid> <offset>命令,其中runid是主服務器運行時ID,offset是當前從服務器的複製偏移量。
前面兩種情況主服務器收到psync命令之後,會出現以下三種可能:
- Redis使用一組哨兵(Sentinel)來監控主從服務節點的可用性;
- 一旦發現Redis主節點失效,將選出一個哨兵作爲節點領導者(Leader)。
- 哨兵領導者再從剩餘的Redis從節點選出一個Redis節點作爲新的主節點繼續對外服務。
以上將Redis節點分爲兩類:
- 哨兵(Sentinel)節點:負責監控節點的運行情況;
- 數據節點:即正常服務客戶端請求的Redis節點,有主從之分。
以上是大體流程,這個流程需要解決以下幾個問題:
- 如何對redis節點進行監控?
- 如何確定一個Redis節點失效?
- 如何選出一個哨兵Leader節點?
- 哨兵節點選出一個新的Redis主節點的依據是什麼?
三個監控任務
哨兵節點通過三個定時監控任務監控Redis數據節點的服務可用性。
- info命令
每隔10秒,每個哨兵節點都會向Redis主從數據節點發送info命令,獲取新的拓撲結構信息。
Redis拓撲結構信息包括了:
- 本節點角色:主或從
- 主從節點的地址或者端口信息。
這樣,info命令就能從主從Redis節點獲取節點信息,因此那些後續才加入的從節點信息不需要顯示配置就能夠感知。
2、向_sentinel_:hello頻道同步信息
每隔兩秒,每個哨兵節點將會向Redis數據節點的_sentinel_:hello頻道同步自身得到的主從節點信息以及當前哨兵節點的信息。
由於其他哨兵節點也訂閱了這個頻道,因此這個操作可以交換哨兵節點之間關於主從節點以及哨兵節點的信息。
這一操作實際上完成兩件事:
- 發現新的哨兵節點:如果有新的哨兵節點加入,此時保存下來這個新哨兵節點的信息,後續與新哨兵節點建立連接。
- 交換主節點的狀態信息,作爲後續客觀判斷主節點下線的依據。
3、向主節點做心跳探測
每隔一秒,每個哨兵節點向主從數據節點以及其他sentinel節點發送ping命令做心跳檢測,這個心跳檢測是後續主觀判斷Redis主節點下線的依據。
主觀下線和客觀下線
1、主觀下線
上面三個監控任務中的第三個探測心跳任務,如果在配置的down-after-millisenconds之後沒有收到有效回覆,那麼就認爲該數據節點“主觀下線(sdown)”。
爲什麼稱爲主觀下線?因爲在一個分佈式系統中,有多個機器在一起聯動工作,網絡可能出現各種狀況,僅憑一個節點的判斷還足以證明該節點已經下線了,這就需要後面的“客觀下線”。
2、客觀下線
當一個哨兵節點認爲主節點主觀下線時,該哨兵節點需要通過“sentinel is-master-down-by addr”命令向其他節點諮詢主節點是否下線了,如果超過半數哨兵節點認爲主節點已經下線了,此時認爲主節點“客觀下線”。
選舉哨兵領導者:
當主節點客觀下線時,需要選舉出一個哨兵節點作爲領導者,已完成後續選擇出的新的主節點的工作。
選舉的大致思路是:
- 每個哨兵節點是通過向其他節點發送“sentinel is-master-down-by addr”命令來哨兵領導者。
- 而每個哨兵節點再收到一個“sentinel is-master-down-by addr”命令之時,只允許給第一個節點投票,其他節點的該命令都會被拒絕。
- 如果一個哨兵節點收到半數以上的同意票,則成爲哨兵領導節點。
- 如果前三步在一定時間內都沒有選出一個哨兵領導者,將 重新開始下一次選舉。
無量虛空神主注:這段我有點糊塗,所以在官方文檔翻譯了這段(SENTINEL get-master-addr-by-name <master name> : 返回給定名字的主服務器的 IP 地址和端口號。 如果這個主服務器正在執行故障轉移操作, 或者針對這個主服務器的故障轉移操作已經完成, 那麼這個命令返回新的主服務器的 IP 地址和端口號。)
可以看出,這個選舉領導者的流程很像Raft中選舉Leader的流程。
選出新的節點:
在剩下的Redis從節點中,按照以下順序選出新的主節點:
- 過濾掉“不健康”的數據節點:比如主觀下線、斷線的從節點、五秒內沒有回覆過哨兵節點ping命令(心跳反應)的節點、與主節點失聯的從節點。
- 選擇slave-priority(從節點優先級)最高的節點,如果存在則返回,如果不存在則繼續後面的流程。
- 到了這裏,所有從節點的狀態都是一樣的,選擇runid最小的節點。
提升新的主節點:
選擇新的主節點之後,還需要最後的流程讓該節點成爲新的主節點:
- 哨兵領導者向上一步選出的從節點發出”slaveof no one”命令,讓該節點成爲主節點;
- 哨兵領導者向剩餘的從節點發送命令,讓它們成爲新主節點的從節點。(failover-state-send-slaveof-noone <instance details> :Sentinel 正在將指定的從服務器升級爲主服務器,等待升級功能完成。)
- 哨兵節點會將原來的主節點更新爲從節點,當其恢復之後,會命令它去複製新主節點的數據。
無量虛空神主注:另外附上官方文檔地址,希望大家可以多讀源文檔。
官方文檔:https://redis.io/documentation
這是中文翻譯比較好的http://redisdoc.com/