redis入門指南(五)—— 複製與哨兵

寫在前面

  學習《redis入門指南》筆記,結合實踐,只記錄重要,明確,屬於新知的相關內容。

 

一、複製

  1、在複製中,數據庫分為兩類,一類主數據庫,一類從數據庫,主庫用來讀寫,從庫用來讀,主庫可以擁有多個從庫,但從庫只能擁有一個主庫。

  2、使用複製非常簡單,只需要在從庫的啟動參數運行時命令配置文件中加入「 slaveof 主庫地址 從庫地址 」即可,主庫無需配置;可通過INFO replication查看主從的複製信息。

  3、從庫默認為只讀,可通過修改配置文件參數slave-read-only為no使其可寫,但從庫的數據不會同步給主庫;當使用slaveof命令修改了複製的主庫,從庫會停止和當前主庫同步,轉而同步新主庫的數據;對於從庫還可以使用SLAVE NO ONE停止接收從庫信息,轉換為主庫。

  4、複製原理:從庫啟動後向主庫發送SYNC命令,主庫收到後開始執行RDB持久化(即使沒有開啟持久化)並緩存持久化期間收到的命令,這兩項任務結束後,會將rdb文件和緩存一併發給從庫,從庫使用臨時文件保存這些數據,然後替換本地rdb文件並載入到內存,這一過程成為複製初始化,最後主庫一旦收到寫命令都會異步發送給從庫。當主從斷開重連後,2.6版本以前會重新執行複製初始化,2.8版本加入了增量複製。

  5、主從複製採用異步方式,命令執行完畢立刻返回給客戶端,之後異步發送命令給從庫,這樣保證了寫入性能,但主從的數據會有一個不一致的時間窗,在命令同步完成前,如果網絡斷開,主庫無法知道多少個從庫同步了數據;通過設置參數min-slaves-to-write(至少有多少個從庫連接才可以寫入) 和min-slaves-max-lag(允許從庫失去連接的最長時間)來降低主從不一致的可能性,這一特性默認關閉。

  6、通過複製可以實現讀寫分離,並且從庫也可以擁有自己的從庫;對於較耗時的操作例如持久化,可以禁用主庫的持久化而開啟從庫的,當從庫崩潰重啟後主庫會自動同步數據過來;當主庫崩潰時,必須先將從庫提升為主庫(通過slaveof no one),再將崩潰的主庫設置為新的主庫的從庫,即可將數據同步。

  注意:當開啟複製,並關掉主庫持久化功能時,若主庫崩潰,一定不能使用監控工具直接重啟主庫,因為主庫沒有持久化,直接重啟會導致主庫數據丟失,並且從庫會跟着複製主庫,使得從庫數據也被清空,失去持久化以及複製的意義。

  7、基於RDB方式的持久化有明顯的缺點,首先當主庫禁用RDB時,依然會生成RDB文件,下次啟動時會以該快照文件啟動,這使得數據可能是任意時間點的,而不是最新的;其次,當硬盤性能較差時,每次和從庫同步,都會執行一次快照對硬盤讀寫,性能會降低。2.8.18版本引入了無硬盤複製(通過參數repl-diskless-sync yes開啟),不再將快照內容存儲到硬盤,而是通過網絡發送給從庫,避免了硬盤性能瓶頸。

  8、redis2.8版本實現了主從庫的增量複製,首先,從庫會存儲主庫的運行id(run id保證唯一),在複製同步階段,主庫每發一個命令,都會將其存放到一個積壓隊列中,並記錄積壓隊列的存放命令的偏移量範圍,然後從庫每次收到命令會記錄下該偏移量,重連時,主庫判斷從庫傳來的運行id是否與自己一致,接着判斷命令偏移量是否在積壓隊列中,如果在則執行增量複製,將積壓隊列中相應的命令發給從庫。如果不滿足以上條件就會全部同步。

  9、增量複製,使用「 PSYNC 主庫運行id 斷開前最新的命令偏移量 」命令代替SYNC,增量複製的過程對於使用者來說時透明的;需要關注的就是積壓隊列(一個循環隊列)的大小,通過參數repl-backlog-size(默認1M)來配置,參數repl-backlog-ttl(默認1小時)用來控制與從庫斷開多久後可以釋放積壓隊列的內存。

 

二、哨兵

  10、哨兵是一個獨立的進程,提供了故障監控和自動化恢復的功能(如主庫停止服務時,將從庫提升為主庫),哨兵可以用來監控數據庫,也可以用來監控其他哨兵。

  11、使用哨兵前,要先建立一個配置文件,如名為sentinel.conf,內容為:

1 格式為:sentinel monitor master-name ip port quorum
2 sentinel monitor mymaster 127.0.0.1 6379 1

  mymaster 表示主庫名字,可以隨便定義,之後是主庫ip,port,1表示最低通過票數。配置哨兵時,只需配置主庫,因為哨兵會主動發現主庫的相關從庫。

  12、哨兵啟動時,會先讀取配置文件,master-name只能由字母、數字、「. – _」組成,quorum表示執行故障恢復操作前至少需要幾個哨兵節點同意;一個哨兵節點可以監控多個主從系統(只需要配置文件多條配置),多個哨兵節點也可以監控同一個主從系統。

  13、哨兵啟動後,會與要監控的數據庫建立兩條連接,其中一條用來訂閱主庫的__sentinel__:hello頻道以獲取同樣監控主庫的其他哨兵的信息,由於訂閱時無法執行其他命令,所以另一條連接用來發送其他命令。

  14、建立連接後,哨兵執行三個操作,每10秒向主庫和從庫發送INFO命令,每2秒向主庫和從庫的__sentinel__:hello發送自己的信息,每1秒向主庫,從庫和其他哨兵節點發送PING。

  15、發送INFO命令,獲得數據庫相關信息,運行id,複製信息等,從庫的信息也在此時獲得,並向每個從庫建立兩條連接,之後根據此信息不斷更新自己的監控隊列。

  16、向頻道__sentinel__:hello發送信息,用來與同樣監控該數據庫的哨兵分享自己的信息,如果發現新哨兵,則向其建立一條連接(用來發送PING,只需要一條連接),消息內容為:

<哨兵的地址>,<哨兵的斷開>,<哨兵的運行id>,<哨兵的配置版本>,<主庫的名字><主庫的地主>,<主庫的端口>,<主庫的配置版本>

  17、發送PING命令,實現了自動發現後,哨兵要通過PING監控節點是否運行,當配置參數down-after-milliseconds的值小於一秒時,會每隔down-after-milliseconds發送PING,大於1秒時,每隔一秒發送。

1 sentinel down-after-milliseconds master-name 60000  // 每隔1秒
2 sentinel down-after-milliseconds master-name 600    // 每隔600毫秒

  18、當超過down-after-milliseconds選項指定的時間後,仍沒有回復,則哨兵認為其已經主觀下線(表示從當前進程來看,該節點已停止服務),如果該節點是主庫,則會判斷是否需要故障恢復,哨兵發送SENTINEL is-master-down-by-addr詢問其他哨兵是否也認為主庫已停止服務,當達到指定數量(quorum參數),則會認為其客觀下線,並選舉領頭哨兵對其執行故障恢復。

  19、選舉領頭哨兵使用Raft算法,發現主庫客觀下線的哨兵向每個節點發送命令。要求對方選擇自己成為領頭哨兵,如果對方沒有選過其他人,則會同意其成為領頭哨兵,當發起選舉的哨兵發現有超過半數且大於quorum個哨兵同意自己成為領頭哨兵,則成功當選,當有多個哨兵時,會出現誰也沒有當選的情況,此時會等待一個隨機時間重新發起參選請求,直到成功;因為要有超過半數的哨兵同意,所以一次只會選舉出一個領頭哨兵。

  20、選出領頭哨兵後,開始進行故障恢復,先從停止服務的主庫的從庫中挑選一個來充當新的主庫,選擇優先級最高的從庫,可通過slave-priority參數配置優先級,如果有多個優先級最高的從庫,則選擇複製的命令偏移量最大的(信息最完整),如果還沒有選出唯一一個,則選擇運行id最小的一個;選出數據庫之後,領頭哨兵發送SLAVE NO ONE使其升為主庫,而向其他從庫發送SLAVEOF命令使其成為新主庫的從庫,最後則是更新哨兵的內部記錄,當舊的主庫也更新為新主庫的從庫,使得當其恢復時以從庫身份運行。

  21、部署哨兵時,應儘可能地使哨兵的視角與每節點一致,如為每一個節點部署哨兵,每個哨兵與其對應的節點網絡應儘可能一致,設置quorum參數為N/2+1(N為哨兵數量);但當節點較多時,會產生大量冗餘連接,或者redis負載較高時,影響其對哨兵的回復或與其他哨兵的通信,所以哨兵配置還要根據實際的生存環境選擇。