Redis高可用中的複製是怎樣實現的?

Redis經過不斷髮展,現在已經作爲一款非常優秀的分佈式緩存數據庫,高性能是它出生便攜帶的強悍特點,但是要作爲分佈式服務,一如優秀的它,也必然少不了負載均衡、高可用等等的特性實現。下面主要談下Redis的複製功能實現。

複製(replicate)

“高可用性”(High Availability)通常來描述一個系統經過專門的設計,從而減少停工時間,而保持其服務的高度可用性。既然要保持其服務的高度可用性,就是容忍突發情況對服務實例災難性的摧毀。對於使用單機的Redis,萬一運行這個Redis實例的機器掛了或者機器網絡原因導致訪問不了,我們依賴到Redis的服務的應用也將不可用。如果想實現高可用,重要方式就是備份,Redis的主從模式就是如此,從服務複製主服務的數據,一旦主服務器掛了,從服務器可以平滑的替換主服務器,能夠無縫地替換的前提就是複製,複製使主從服務器保存相同的數據。複製我認爲是Redis實現高可用的基石。

體驗複製功能的使用過程

體驗複製功能的過程其實很簡單,首先安裝完Redis,用戶可以啓動兩個不同端口的實例,然後通過salveof命令設置slaveof配置選項,讓一個服務器去複製另一個服務器。

  1. 分別啓動7001、7002端口的Redis服務器實例
redis-server --port 7001
redis-server --port 7002
  1. 連接7001端口Redis,發送slaveof 127.0.0.1 7002命令

連接:

redis-cli -p 7001
slaveof 127.0.0.1 7002

這樣,就完成7001端口Redis成爲7002端口Redis從服務器的設置,然後通過在主服務器執行set number 12345命令,看看從服務器是否能順利獲取number的值

  1. 主服務執行set number 12345,從服務器執行get number命令
    在這裏插入圖片描述
    在這裏插入圖片描述
    從執行效果來看,可以得出7001端口從服務器能順利從7002端口主服務器複製數據的結論。

一、舊版複製功能的實現

Redis的複製功能分爲同步(sync)和命令傳播(command),隨着Redis版本發展,複製功能的實現分爲了舊版(2.8版本以前)的實現,新版(2.8版本之後)實現。

Redis的複製功能分爲同步(sync)和命令傳播(command propagate)兩個操作:

  1. 同步操作用於將從服務器的數據庫狀態更新至主服務器當前所在的數據庫狀態。
  2. 命令傳播操作,字面的意思就是主服務器把對數據庫狀態造成影響的寫命令傳播到從服務器,讓從服務器始終和主服務器數據庫狀態保持一致

1.1 同步

進行同步的操作很簡單,就是往從服務器發送slaveof命令,命令從服務器作爲某個服務器的slave,比如上面就展示了,連接7001端口Redis,發送slaveof 127.0.0.1 7002命令過程和效果。
對從服務器發送slaveof命令後,從服務器對主服務器的同步操作是通過發送sync命令來完成的,以下是sync命令的執行步驟。

  1. 從服務向主服務器發送sync命令。
  2. 收到sync命令的主服務器執行bgsave命令,在後臺生成一個RDB文件,並使用一個緩衝區記錄從現在開始執行的所有寫命令。
  3. 當主服務的bgsave命令執行完畢時,主服務器會將bgsave命令生成的RDB文件發送給從服務器,從服務器接受並載入這個RDB文件,將自己的數據庫狀態更新至服務器執行bgsave時的數據庫狀態。
  4. 主服務器將記錄在緩衝區裏面的所有寫命令發送給從服務器,從服務執行這些寫命令,將數據庫狀態同步更新到和主服務一樣的數據庫狀態。

1.2 命令傳播

在同步操作執行完後,主從服務器數據庫在這個時候達成了暫時的一致性,爲什麼說暫時呢?畢竟主服務器還會持續的執行寫命令,這些寫命令產生的數據變化,是通過命令傳播方式同步給從服務器的。
但是舊版命令傳播功能在斷線後,然後重新連接複製上會存在一個很大的問題,看下面的斷線重複制過程。
在這裏插入圖片描述
從上圖可以看到,在T10091主從服務器斷線,重新連接,T10092從服務器會發送SYNC命令,主服務器在T10093時候會再走一次同步複製的老路,不是從T10090執行的SET k10089 v10089命令開始重新傳播,而是從剛開始執行命令傳播的set k1 v1命令開始。如果在主從服務器斷線期間,主服務器執行的寫命令成千上萬的,這就很低效要命了。這種低效的感覺,類似於我們平常下載一個2g的電影,下載到99%,突然網絡斷線失敗要重新下載的無力感。當然網絡下載得益於斷點上傳和斷點下載的支持,不再存在我說的因網絡斷線要重新下載的情況了。那麼類比下下載,Redis在傳播命令上能否也能實現和斷點上傳的效果呢?答案肯定是能的。

二、新版複製功能的實現

從上面介紹可以瞭解到舊版複製功能在處理斷線重複制情況時存在低效問題,Redis從2.8版本開始,使用PSYNC命令代替SYNC命令來執行復制時的同步操作,從而解決了舊版複製功能的低效問題。其實稍加思考下,解決這個問題的思路很簡單,我們下載東西的時候常常會看到下載過程中顯示的進度條,如果在同步完成後進行命令傳播階段,主從服務器能夠雙方維護一個此刻進度的偏移量(位置),如果斷線,就可以根據這個偏移量開始繼續傳命令,則不必從頭開始了。具體實現看下面。

2.1 PSYNC命令

PSYNC命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)兩種模式:

  • 完整重同步和SYNC命令的執行步驟基本一樣,它們都是通過讓主服務器創建併發送RDB文件,以及向從服務器發送保存在緩衝區裏面的寫命令來進行同步。
  • 部分重同步則用於處理斷線後重複製情況,當從服務器在斷線後重新連接主服務器時,如果條件允許,主服務器將主從服務器連接斷開期間執行的寫命令發送給從服務器,從服務器只要接收並執行這些寫命令,就可以將數據庫更新至主服務器當前所處的狀態。
    PSYNC命令的部分重同步模式很好的解決了舊版複製功能在處理斷線後重複製時出現的低效情況,下圖展示了,PSYNC命令的解決過程
    在這裏插入圖片描述
    我們可以看到在PSYNC模式,主從服務器斷線重連後,從服務器會向主服務器發送PSYNC命令,主服務器會向從服務器返回+CONTINUE回覆,表示執行部分重同步,然後從服務器接收+CONUTINUE回覆,準備執行部分重同步…
    關鍵過程還是在於T10092 到 T10094的實現細節。

2.2 部分重同步實現細節

部分重同步功能由以下三個部分構成:

  • 主服務器的複製偏移量(replication offset)和從服務器的複製偏移量。
  • 主服務器的複製積壓緩衝區(replication backlog)。
  • 服務器的運行(run ID)。

我們其實可以嘗試根據這三個部分腦補下部分重同步的實現,怎樣能在斷線恢復後,從斷線前一刻執行的命令開始繼續進行命令傳播呢?這個很簡單,在主服務器每次命令傳播時,主從服務器都維護下複製的偏移量就好,主服務器每次傳播N個字節數據,就將自己的複製偏移量的值加上N,從服務器接收到N個字節時,就將自己的複製量加上N。當斷線重連時候,就能在斷線前的複製偏移量開始傳播命令,但是斷線到重連後這個時間段主服務執行的寫命令咋辦?主服務器搞一個複製積壓緩衝區把斷線期間執行的命令存起來就好啦,當執行部分重同步時,主服務器根據從服務器重連自己後發來的複製偏移量,對比自己維護的複製偏移量,可以繼續傳播斷線期間,從服務器沒有接受到的在主服務的執行命令。說到這裏,貌似有了複製偏移量和複製積壓緩存區就可以實現了,服務器運行ID有什麼用?每個Redis服務器都有一個運行ID,運行ID是在服務器啓動時候產生的,由40個16進制字符組成,當從服務器對主服務器進行初次複製時,主服務器會將自己的運行ID傳送給從服務器,從服務器會將這個運行ID保存下來,當從服務器斷線重新連接上主服務器時候,從服務會把這個運行ID傳給主服務器,主服務接受到這個運行ID,會判斷下是不是自己的運行ID,如果是,說明是斷線重複制場景,否則就是完整重同步場景,而進行相應的處理。

後記

到此爲止,Redis的複製功能原理就介紹到這裏了,複製是Redis實現高可用的基石,具有非常重要的意義。Redis從單機版慢慢演進到支持高可用、集羣橫向擴展,實在是很優秀的一款中間件。另外從複製功能可以看到,Redis有些功能並不是一開始就設計很完美的,都是不斷完善改進的,數據結構的設計。學習Redis不單單是對工作使用上有幫助,它的不斷演進優化設計思想也對我們有很大的啓發。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章