Redis系列(四):Redis的複製機制(主從複製)

来源:https://www.cnblogs.com/zwwhnly/archive/2020/04/07/12651527.html
-Advertisement-
Play Games

本篇博客是Redis系列的第4篇,主要講解下Redis的主從複製機制。 本系列的前3篇可以點擊以下鏈接查看: "Redis系列(一):Redis簡介及環境安裝" "Redis系列(二):Redis的5種數據結構及其常用命令" "Redis系列(三):Redis的持久化機制(RDB、AOF)" Red ...


本篇博客是Redis系列的第4篇,主要講解下Redis的主從複製機制。

本系列的前3篇可以點擊以下鏈接查看:

Redis系列(一):Redis簡介及環境安裝

Redis系列(二):Redis的5種數據結構及其常用命令

Redis系列(三):Redis的持久化機制(RDB、AOF)

Redis的主從複製是面試中經常會被問的,我最近面試的幾家公司只要聊到Redis,都會問我主從複製的原理。

1. 為什麼需要主從複製?

在本系列的上一篇博客中,我們講到了Redis的持久化機制,它很好的解決了單台Redis伺服器由於意外情況導致Redis伺服器進程退出或者Redis伺服器宕機而造成的數據丟失問題。

但持久化機制還原數據有個前提:你的Redis伺服器得能正常啟動。

如果遇到極端的斷電情況(雖然概率小,但是有可能),Redis伺服器啟都啟動不了,怎麼還原數據?怎麼保證它的高可用。

就算Redis伺服器能啟動了,網路連接也有崩掉的可能,我不信你沒看到過電纜被挖斷導致的某些服務不可用的新聞。

正是由於有這樣的風險,所以生產環境Redis伺服器不可能使用單台的,那既然使用多台Redis伺服器,多台Redis伺服器之間的數據如何同步呢?

這就需要用到Redis的複製機制。

還有個原因就是,雖然Redis的性能很好,但單台畢竟還是有瓶頸的,使用主從複製可以實現讀寫分離,提高Redis的高可用性,即主伺服器用來執行寫命令,多個從伺服器用來執行讀命令,類似於資料庫的讀寫分離。

綜上所述,主從複製主要有以下2個使用場景:

  1. 數據備份
  2. 讀寫分離

2. 主從複製實踐

首先,我在本機開啟2個Redis實例(也可以搞2台Redis伺服器),分別為127.0.0.1:6379、127.0.0.1:6380。

然後,使用redis-cli連接Redis實例127.0.0.1:6380並執行如下命令:

SLAVEOF 127.0.0.1 6379

此時,我們稱127.0.0.1:6379為127.0.0.1:6380的主伺服器(master),稱127.0.0.1:6380為127.0.0.1:6379的從伺服器(slave)

2者之間的關係如下所示:

然後,我們在主伺服器上執行如下寫命令:

SET msg "hello world"

此時,我們不僅能在主伺服器上獲取到該值,也能在從伺服器上獲取到該值:

然後,我們在主伺服器上執行如下刪除命令:

DEL msg

此時,我們會發現不僅主伺服器上的msg鍵被刪除,從伺服器上的msg也被刪除:

所以說,進行複製中的主從伺服器雙方的資料庫將保存相同的數據

值得註意的是,從伺服器只能執行讀命令,執行寫命令時會報如下錯誤:

如果從伺服器不想再複製主伺服器,可以執行命令:SLAVEOF no one

3. 舊版複製功能的實現(SYNC)

這裡的舊版指的是Redis 2.8以前的版本。

Redis的複製功能分為以下2個操作:

  1. 同步:用於將從伺服器的資料庫狀態更新至主伺服器當前所處的資料庫狀態。
  2. 命令傳播:用於在主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態不一致時,讓主從伺服器的資料庫狀態重新回到一致狀態。

3.1 同步

當客戶端向從伺服器發送SLAVEOF命令,要求從伺服器複製主伺服器時,從伺服器會向主伺服器SYNC命令,該命令的執行步驟如下所示:

  1. 從伺服器向主伺服器發送SYNC命令。
  2. 主伺服器收到SYNC命令後,執行BGSAVE命令,在後臺生成RDB文件,並使用一個緩衝區記錄從現在開始執行的所有寫命令。
  3. 當主伺服器的BGSAVE命令執行完成,主伺服器將生成的RDB文件發送給從伺服器,從伺服器接收並載入這個RDB文件,至此,從伺服器的資料庫狀態和主伺服器執行BGSAVE命令時的資料庫狀態一致。
  4. 主伺服器將記錄在緩衝區裡面的所有寫命令發送給從伺服器,從伺服器接收並執行這些寫命令,至此,從伺服器的資料庫狀態和主伺服器當前的資料庫狀態一致。

SYNC命令執行期間,主從伺服器的通信過程如下圖所示:

3.2 命令傳播

同步操作執行完畢後,主從伺服器的資料庫狀態達到一致狀態,當主伺服器執行了客戶端發送的寫命令時,主伺服器的資料庫就被修改了,導致主從伺服器的資料庫狀態不再一致。

為了讓主從伺服器的資料庫狀態再次回到一致狀態,主伺服器需要對從伺服器執行命令傳播操作:主伺服器會將自己執行的寫命令,發送給從伺服器執行,當從伺服器執行了相同的寫命令後,主從伺服器的資料庫狀態再次回到一致狀態。

舉個具體的例子,比如主從伺服器剛開始都擁有k1、k2、k3、k4、k5這5個鍵,然後客戶端往主伺服器發送了命令DEL k3,此時主伺服器會執行該條命令,並將該條命令傳播給從伺服器執行,從而使主從伺服器的資料庫狀態保持一致。

整個變化過程如下所示:

4. 舊版複製功能的缺陷

這裡的舊版指的是Redis 2.8以前的版本。

在Redis 2.8以前,從伺服器對主伺服器的複製分為以下2種情況:

  1. 初次複製

    從伺服器以前沒有複製過任何主伺服器,或者從伺服器當前要複製的主伺服器和上一次複製的主伺服器不同。

  2. 斷線後重覆制

    處於命令傳播階段的主從伺服器因為網路原因而中斷了複製,但從伺服器通過重試又重新連上了主伺服器,並繼續複製主伺服器。

舊版複製功能可以很好的完成初次複製,但完成斷線後重覆制的效率卻很低。

舉個具體的例子,從伺服器B一直在複製著主伺服器A,剛開始都是正常的,主伺服器A執行的寫命令也都通過命令

傳播的方式傳遞給了從伺服器B執行,但突然因為網路原因,主伺服器A和從伺服器B之間中斷了複製,在這期間,

假設主伺服器又執行了10個寫命令,然後從伺服器B通過重試又重新連上了主伺服器A,繼續開始複製,那麼它是

怎麼複製的呢?

從伺服器B會向主伺服器A發送SYNC命令,主伺服器A接收到命令後會執行BGSAVE命令,BGSAVE命令執行期間的

所有寫命令會被記錄到緩衝區,待BGSAVE命令執行完畢後,主伺服器A會將生成的RDB文件發送給從伺服器B,

從伺服器B接收並載入這個RDB文件,然後主伺服器A將緩衝區里的寫命令發送給從伺服器B執行,至此,主從

伺服器的資料庫狀態又恢復一致,後續又進入命令傳播階段。

也就是說,每次斷線後重覆制,都要執行一次SYNC命令來一次全量複製,但其實從伺服器B需要的只是斷開連接期間主伺服器A執行的寫命令,按上面的例子,也就是只需要10個寫命令即可。

SYNC命令又是一個非常耗費資源的操作:

  1. 主伺服器需要執行BGSAVE命令生成RDB文件,這會耗費主伺服器大量的CPU、記憶體和磁碟IO資源。
  2. 主伺服器需要將生成的RDB文件發送給從伺服器,這會耗費主從伺服器大量的網路資源(帶寬和流量)。
  3. 接收到RDB文件的從伺服器需要載入RDB文件,在載入期間,從伺服器會阻塞,沒辦法處理命令請求。

5. 新版複製功能的實現(PSYNC)

這裡的新版指的是Redis 2.8以及之後的版本。

從Redis 2.8版本開始,Redis使用PSYNC命令代替SYNC命令來執行複製時的同步操作。

PSYNC命令有以下2種場景:

  1. 完整重同步

    完整重同步用於處理初次複製,執行步驟和SYNC命令的執行步驟基本一樣。

  2. 部分重同步

    部分重同步用於處理斷線後重覆制,當從伺服器在斷線後重新連接主伺服器時,如果條件允許,主伺服器可以將主從伺服器連接斷開期間執行的寫命發送給從伺服器,從伺服器只要接收並執行這些寫命令,就可以將資料庫更新至主伺服器當前所處的狀態。

仍然用上面舉的例子,新版複製,主伺服器只需要把斷開期間執行的10個寫命令發送給從伺服器即可,而不用生成併發送整個RDB文件,性能大大提升。

主從伺服器在執行部分重同步時的通信過程如下圖所示:

那麼部分重同步是如何實現的呢?

部分重同步功能由以下3個部分組成:

  1. 主伺服器和從伺服器的複製偏移量
  2. 主伺服器的複製積壓緩衝區
  3. 伺服器的運行ID

接下來我們一一講解。

5.1 複製偏移量

執行複製的主伺服器和從伺服器會分別維護一個複製偏移量:

  1. 主伺服器每次向從伺服器傳播N個位元組的數據時,就將自己的複製偏移量的值加上N。
  2. 從伺服器每次收到主伺服器傳播來的N個位元組的數據時,就將自己的複製偏移量的值加上N。

舉個例子,假設主伺服器有3個從伺服器,它們的複製偏移量都為10086,如下圖所示:

然後,主伺服器向3個從伺服器傳播了長度為33位元組的數據,那麼主伺服器的複製偏移量會加上33,變為10119,

從伺服器A在這時剛好斷線了,沒有接收到數據,所以偏移量仍然為10086,

從伺服器B和從伺服器C正常接收到了數據,所以偏移量都更新為了10019,如下圖所示:

很顯然,通過對比主從伺服器的複製偏移量,可以很容易地知道主從伺服器是否處於一致狀態。

然後,從伺服器A通過重試又重新連接到了主伺服器,然後向主伺服器發送PSYNC命令,並報告了自己當前的複製

偏移量為10086,主伺服器此時需要處理2個問題:

  1. 該對從伺服器A執行完整重同步還是部分重同步?
  2. 如果執行部分重同步,主伺服器從哪裡獲取到斷線期間從伺服器A丟失的數據?

帶著這2個問題,我們看下複製積壓緩衝區。

5.2 複製積壓緩衝區

複製積壓緩衝區是主伺服器維護的一個固定長度先進先出隊列,預設大小為1MB。

當主伺服器進行命令傳播時,它不僅會將寫命令發送給所有從伺服器,還會將寫命令入隊到複製積壓緩衝區,如下圖所示:

所以,主伺服器的複製積壓緩衝區會保存著一部分最近傳播的寫命令,並且為隊列中的每個位元組記錄相應的複製偏移量,如下所示:

偏移量 ... 10087 10088 10089 10090 10091 ...
位元組值 ... '*' 3 '\r' '\n' '$' ...

當從伺服器重新連接上主伺服器時,會通過PSYNC命令將自己的複製偏移量offset發送給主伺服器,主伺服器會根據以下規則來決定對從伺服器執行何種同步操作:

  • 如果offset偏移量之後的數據仍然存在於複製積壓緩衝區,那麼主伺服器將對從伺服器執行部分重同步操作。
  • 如果offset偏移量之後的數據已經不存在於複製積壓緩衝區,那麼主伺服器將對從伺服器執行完整重同步操作。

回到之前的例子:

  1. 從伺服器A重新連接上主伺服器,向主伺服器發送PSYNC命令,報告自己的複製偏移量為10086。
  2. 主伺服器收到PSYNC命令以及偏移量10086之後,會檢查偏移量10086之後的數據是否存在於複製積壓緩衝區,結果發現數據還在,於是主伺服器向從伺服器A發送+CONTINUE回覆,表示數據同步將以部分重同步模式來進行。
  3. 接著主伺服器會將複製積壓緩衝區里10086偏移量之後的所有數據(偏移量為10087到10119)都發送給從伺服器A。
  4. 從伺服器A接收這33位元組的缺失數據,就回到與主伺服器一致的狀態。

5.3 伺服器運行ID

每個Redis伺服器,不論主伺服器還是從伺服器,都會有自己的運行ID,運行ID在伺服器啟動時自動生成,由40個十六進位字元組成,如下圖所示:

當從伺服器對主伺服器進行初次複製時,主伺服器會將自己的運行ID傳送給從伺服器,從伺服器會將這個運行ID保存起來。

當從伺服器斷線並重新連接上主伺服器時,從伺服器會把之前保存的運行ID發送給當前連接的主伺服器:

  • 如果從伺服器之前保存的運行ID和當前連接的主伺服器的運行ID相同,說明從伺服器斷線前後複製的是同一臺主伺服器,主伺服器可以繼續嘗試執行部分重同步操作。
  • 如果從伺服器之前保存的運行ID和當前連接的主伺服器的運行ID不相同,說明從伺服器斷線前後複製的不是同一臺主伺服器,主伺服器將對從伺服器執行完整重同步操作。

5.4 PSYNC命令執行細節

對於從伺服器來說,調用PSYNC命令有以下2種情況:

  1. 如果從伺服器以前沒有複製過任何主伺服器,或者之前執行過SLAVEOF on one命令,那麼從伺服器在開始一次新的複製時將向主伺服器發送PSYNC ? -1命令,主動請求主伺服器進行完整重同步。

  2. 如果從伺服器已經複製過某個主伺服器,那麼從伺服器在開始一次新的複製時將向主伺服器發送

    PSYNC {runid} {offset}命令,其中runid是上一次複製的主伺服器的運行ID,offset是從伺服器當前的複製偏移量。

對於主伺服器來說,接收到PSYNC命令後會向從伺服器返回以下3種回覆中的一種:

  1. 如果主伺服器返回+FULLRESYNC {runid} {offset},表示主伺服器將與從伺服器執行完整重同步操作,其中runid是主伺服器的運行ID,從伺服器會將這個ID保存起來,在下一次發送PSYNC命令時使用,offset是主伺服器當前的複製偏移量,從伺服器會將這個值作為自己的初始化偏移量。
  2. 如果主伺服器返回+CONTINUE,表示主伺服器將與從伺服器執行部分重同步操作,主伺服器會將從伺服器缺少的那部分數據發送給從伺服器。
  3. 如果主伺服器返回-ERROR,表示主伺服器的版本低於Redis 2.8,它識別不了PSYNC命令,從伺服器將向主伺服器發送SYNC命令,並與主伺服器執行完整重同步操作。

以上描述流程可以使用以下流程圖來表示:

6. 源碼及參考

黃健巨集 《Redis設計與實現》


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、redis 簡介 1、什麼是 redis? redis 是一個開源免費的高性能的 key - value 資料庫。 支持數據持久化,即可以將記憶體的數據存儲在磁碟中,重啟服務後可以再次載入磁碟中的數據並使用。 支持多種類型,比如:String(字元串)、List(列表)、Set(集合)、zset( ...
  • MySQL複雜查詢使用實例 By:授客 QQ:1033553122 表結構設計 SELECT id, `name`, parent_id FROM `tb_testcase_suite` 說明: parent_id值關聯表自身id列的值,如果其值為-1,則表示該記錄不存在父級記錄,否則表示該記錄存在 ...
  • 原因:在數據查詢中replace函數無法對錶table中text/ntext類型的欄位colname進行了字元串操作。 解決方法:將text當作varchar(實際內容長度低於8000位元組時)或把ntext當作nvarchar(實際內容長度低於4000位元組時)。 但是當text欄位內容長度超過800 ...
  • 11. 獲取所有員工當前的manager 獲取所有員工當前的manager,如果當前的manager是自己的話結果不顯示,當前表示to_date='9999 01 01'。結果第一列給出當前員工的emp_no,第二列給出其manager對應的manager_no。 sql CREATE TABLE ...
  • 1. 查找最晚入職員工的所有信息 sql CREATE TABLE ( int(11) NOT NULL, date NOT NULL, varchar(14) NOT NULL, varchar(16) NOT NULL, char(1) NOT NULL, date NOT NULL, PRIM ...
  • SET key value [EX seconds] [PX milliseconds] [NX|XX] EX seconds : 將鍵的過期時間設置為 seconds 秒。 執行 SET key value EX seconds 的效果等同於執行 SETEX key seconds value 。 ...
  • 大數據技術開篇之Hadoop入門【hdfs】 學習都是從瞭解到熟悉的過程,而學習一項新的技術的時候都是從這個技術是什麼?可以乾什麼?怎麼用?如何優化?這幾點開始。今天這篇文章分為兩個部分。一、hadoop概述 二、hadoop核心技術之一的hdfs的講解。 【hadoop概述】 一、hadoop是什 ...
  • 相關詞語: redo log:日誌模塊(臨時記錄,類似於便簽),InnoDB 引擎特有日誌 WAL(Write-Ahead Logging):寫入方式 binlog:日誌模塊(歸檔日誌),Server 層的日誌 crash-safe:redo log帶來的好處(MySQL 可以恢復到固定時間內任意一 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...