十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
主從架構(gòu)可以說是互聯(lián)網(wǎng)必備的架構(gòu)了,第一是為了保證服務(wù)的高可用,第二是為了實現(xiàn)讀寫分離,你可能熟悉我們常用的 MySQL 數(shù)據(jù)庫的主從架構(gòu),對于我們 redis 來說也不意外,redis 數(shù)據(jù)庫也有各種各樣的主從架構(gòu)方式,在主從架構(gòu)中會涉及到主節(jié)點與從節(jié)點之間的數(shù)據(jù)同步,這個數(shù)據(jù)同步的過程在 redis 中叫做復(fù)制,這在篇文章中,我們詳細的聊一聊 redis 的復(fù)制技術(shù)和主從架構(gòu) ,本文主要有以下內(nèi)容:
主從架構(gòu)環(huán)境搭建
主從架構(gòu)的斷開
復(fù)制技術(shù)的原理
心跳檢測
主從拓撲架構(gòu)
redis 的實例在默認的情況下都是主節(jié)點,所以我們需要修改一些配置來搭建主從架構(gòu),redis 的主從架構(gòu)搭建還是比較簡單的,redis 提供了三種方式來搭建主從架構(gòu),在后面我們將就介紹,在介紹之前我們要先了解主從架構(gòu)的特性:在主從架構(gòu)中有一個主節(jié)點(master)和最少一個從節(jié)點(slave),并且數(shù)據(jù)復(fù)制是單向的,只能從主節(jié)點復(fù)制到從節(jié)點,不能由從節(jié)點到主節(jié)點。
主從架構(gòu)的建立有以下三種方式:
上面三種方式都可以搭建 Redis 主從架構(gòu),我們以第一種方式來演示,其他兩種方式自行嘗試,由于是演示,所以就在本地啟動兩個 Redis 實例,并不在多臺機器上啟動 redis 的實例了,我們準備一個端口 6379 的主節(jié)點實例,準備一個端口 6480 從節(jié)點的實例,端口 6480 的 redis 實例配置文件取名為?6480.conf
并且在里面添加 slaveof 語句,在配置文件最后加入如下一條語句
slaveof 127.0.0.1 6379
分別啟動兩個 redis 實例,啟動之后他們會自動建立主從關(guān)系,關(guān)于這背后的原理,我們后面在詳細的聊一聊,先來驗證一下我們的主從架構(gòu)是否搭建成功,我們先在 6379 master 節(jié)點上新增一條數(shù)據(jù):
然后再 6480 slave 節(jié)點上獲取該數(shù)據(jù):
可以看出我們在 slave 節(jié)點上已經(jīng)成功的獲取到了在 master 節(jié)點新增的值,說明主從架構(gòu)已經(jīng)搭建成功了,我們使用 info replication 命令來查看兩個節(jié)點的信息,先來看看主節(jié)點的信息
可以看出 6379 端口的實例 role 為 master,有一個正在連接的實例,還有其他運行的信息,我們再來看看 6480 端口的 redis 實例信息
可以看出兩個節(jié)點之間相互記錄著對象的信息,這些信息在數(shù)據(jù)復(fù)制時候?qū)玫健T谶@里有一點需要說明一下,默認情況下 slave 節(jié)點是只讀的,并不支持寫入,也不建議開啟寫入,我們可以驗證一下,在 6480 實例上寫入一條數(shù)據(jù)
127.0.0.1:6480> set x 3
提示只讀,并不支持寫入操作,當(dāng)然我們也可以修改該配置,在配置文件中?replica-read-only yes
配置項就是用來控制從服務(wù)器只讀的,為什么只能只讀?因為我們知道復(fù)制是單向的,數(shù)據(jù)只能由 master 到 slave 節(jié)點,如果在 salve 節(jié)點上開啟寫入的話,那么修改了 slave 節(jié)點的數(shù)據(jù), master 節(jié)點是感知不到的,slave 節(jié)點的數(shù)據(jù)并不能復(fù)制到 master 節(jié)點上,這樣就會造成數(shù)據(jù)不一致的情況,所以建議 slave 節(jié)點只讀。
主從架構(gòu)的斷開同樣是 slaveof 命令,在從節(jié)點上執(zhí)行 slaveof no one 命令就可以與主節(jié)點斷開追隨關(guān)系,我們在 6480 節(jié)點上執(zhí)行 slaveof no one 命令
127.0.0.1:6480> slaveof no one
執(zhí)行完 slaveof no one 命令之后,6480 節(jié)點的角色立馬恢復(fù)成了 master ,我們再來看看時候還和 6379 實例連接在一起,我們在 6379 節(jié)點上新增一個 key-value
127.0.0.1:6379> set y 3
在 6480 節(jié)點上 get y
127.0.0.1:6480> get y
在 6480 節(jié)點上獲取不到 y ,因為 6480 節(jié)點已經(jīng)跟 6379 節(jié)點斷開的聯(lián)系,不存在主從關(guān)系了,slaveof 命令不僅能夠斷開連接,還能切換主服務(wù)器,使用命令為?slaveof {newMasterIp} {newMasterPort}
,我們讓 6379 成為 6480 的從節(jié)點, 在 6379 節(jié)點上執(zhí)行?slaveof 127.0.0.1 6480
命令,我們在來看看 6379 的 info replication
127.0.0.1:6379> info replication
6379 節(jié)點的角色已經(jīng)是 slave 了,并且主節(jié)點的是 6480 ,我們可以再看看 6480 節(jié)點的 info replication
127.0.0.1:6480> info replication
在 6480 節(jié)點上有 6379 從節(jié)點的信息,可以看出 slaveof 命令已經(jīng)幫我們完成了主服務(wù)器的切換。
redis 的主從架構(gòu)好像很簡單一樣,我們就執(zhí)行了一條命令就成功搭建了主從架構(gòu),并且數(shù)據(jù)復(fù)制也沒有問題,使用起來確實簡單,但是這背后 redis 還是幫我們做了很多的事情,比如主從服務(wù)器之間的數(shù)據(jù)同步、主從服務(wù)器的狀態(tài)檢測等,這背后 redis 是如何實現(xiàn)的呢?接下來我們就一起看看
我們執(zhí)行完 slaveof 命令之后,我們的主從關(guān)系就建立好了,在這個過程中, master 服務(wù)器與 slave 服務(wù)器之間需要經(jīng)歷多個步驟,如下圖所示:
slaveof 命令背后,主從服務(wù)器大致經(jīng)歷了七步,其中權(quán)限驗證這一步不是必須的,為了能夠更好的理解這些步驟,就以我們上面搭建的 redis 實例為例來詳細聊一聊各步驟。
在 6480 的客戶端向 6480 節(jié)點服務(wù)器發(fā)送?slaveof 127.0.0.1 6379
命令時,我們會立馬得到一個 OK
127.0.0.1:6480> slaveof 127.0.0.1 6379
這時候數(shù)據(jù)復(fù)制工作并沒有開始,數(shù)據(jù)復(fù)制工作是在返回 OK 之后才開始執(zhí)行的,這時候 6480 從節(jié)點做的事情是將給定的主服務(wù)器 IP 地址 127.0.0.1 以及端口 6379 保存到服務(wù)器狀態(tài)的 masterhost 屬性和 masterport 屬性里面
在 slaveof 命令執(zhí)行完之后,從服務(wù)器會根據(jù)命令設(shè)置的 IP 地址和端口,跟主服務(wù)器創(chuàng)建套接字連接, 如果從服務(wù)器能夠跟主服務(wù)器成功的建立 socket 連接,那么從服務(wù)器將會為這個 socket 關(guān)聯(lián)一個專門用于處理復(fù)制工作的文件事件處理器,這個處理器將負責(zé)后續(xù)的復(fù)制工作,比如接受全量復(fù)制的 RDB 文件以及服務(wù)器傳來的寫命令。同樣主服務(wù)器在接受從服務(wù)器的 socket 連接之后,將為該 socket 創(chuàng)建一個客戶端狀態(tài),這時候的從服務(wù)器同時具有服務(wù)器和客戶端兩個身份,從服務(wù)器可以向主服務(wù)器發(fā)送命令請求而主服務(wù)器則會向從服務(wù)器返回命令回復(fù)。
從服務(wù)器與主服務(wù)器連接成功后,做的第一件事情就是向主服務(wù)器發(fā)送一個 ping 命令,發(fā)送 ping 命令主要有以下目的:
在發(fā)送 ping 命令之后,正常情況下主服務(wù)器會返回 pong 命令,接受到主服務(wù)器返回的 pong 回復(fù)之后就會進行下一步工作,如果沒有收到主節(jié)點的 pong 回復(fù)或者超時,比如網(wǎng)絡(luò)超時或者主節(jié)點正在阻塞無法響應(yīng)命令,從服務(wù)器會斷開復(fù)制連接,等待下一次定時任務(wù)的調(diào)度。
從服務(wù)器在接收到主服務(wù)器返回的 pong 回復(fù)之后,下一步要做的事情就是根據(jù)配置信息決定是否需要身份驗證:
在需要身份驗證的情況下,從服務(wù)器將就向主服務(wù)器發(fā)送一條 auth 命令,命令參數(shù)為從服務(wù)器 masterauth 選項的值,舉個例子,如果從服務(wù)器的配置里將 masterauth 參數(shù)設(shè)置為:123456,那么從服務(wù)器將向主服務(wù)器發(fā)送 auth 123456 命令,身份驗證的過程也不是一帆風(fēng)順的,可能會遇到以下幾種情況:
所有的錯誤情況都會令從服務(wù)器中止當(dāng)前的復(fù)制工作,并且要從建立 socket 開始重新發(fā)起復(fù)制流程,直到身份驗證通過或者從服務(wù)器放棄執(zhí)行復(fù)制為止
在身份驗證通過后,從服務(wù)器將執(zhí)行 REPLCONF listening
數(shù)據(jù)復(fù)制是最復(fù)雜的一塊了,由 psync 命令來完成,從服務(wù)器會向主服務(wù)器發(fā)送一個 psync 命令來進行數(shù)據(jù)同步,在 redis 2.8 版本以前使用的是 sync 命令,除了命令不同之外,在復(fù)制的方式上也有很大的不同,在 redis 2.8 版本以前使用的都是全量復(fù)制,這對主節(jié)點和網(wǎng)絡(luò)會造成很大的開銷,在 redis 2.8 版本以后,數(shù)據(jù)同步將分為全量同步和部分同步。
全量復(fù)制:一般用于初次復(fù)制場景,不管是新舊版本的 redis 在從服務(wù)器第一次與主服務(wù)連接時都將進行一次全量復(fù)制,它會把主節(jié)點的全部數(shù)據(jù)一次性發(fā)給從節(jié)點,當(dāng)數(shù)據(jù)較大時,會對主節(jié)點和網(wǎng)絡(luò)造成很大的開銷,redis 的早期版本只支持全量復(fù)制,這不是一種高效的數(shù)據(jù)復(fù)制方式
redis 之所以能夠支持全量復(fù)制和部分復(fù)制,主要是對 sync 命令的優(yōu)化,在 redis 2.8 版本以后使用的是一個全新的 psync 命令,命令格式為:psync {runId} {offset},這兩個參數(shù)的意義:
也許你對上面的 runid、offset 比較陌生,沒關(guān)系,我們先來看看下面三個概念:
1、復(fù)制偏移量
參與復(fù)制的主從節(jié)點都會分別維護自身復(fù)制偏移量:主服務(wù)器每次向從服務(wù)器傳播 N 個字節(jié)的數(shù)據(jù)時,就將自己的偏移量的值加上 N,從服務(wù)器每次接收到主服務(wù)器傳播的 N個字節(jié)的數(shù)據(jù)時,將自己的偏移量值加上 N。通過對比主從服務(wù)器的復(fù)制偏移量,就可以知道主從服務(wù)器的數(shù)據(jù)是否一致,如果主從服務(wù)器的偏移量總是相同,那么主從數(shù)據(jù)一致,相反,如果主從服務(wù)器兩個的偏移量并不相同,那么說明主從服務(wù)器并未處于數(shù)據(jù)一致的狀態(tài),比如在有多個從服務(wù)器時,在傳輸?shù)倪^程中某一個服務(wù)器離線了,如下圖所示:
由于從服務(wù)器A 在數(shù)據(jù)傳輸時,由于網(wǎng)絡(luò)原因掉線了,導(dǎo)致偏移量與主服務(wù)器不一致,那么當(dāng)從服務(wù)器A 重啟并且與主服務(wù)器連接成功后,重新向主服務(wù)器發(fā)送 psync 命令,這時候數(shù)據(jù)復(fù)制應(yīng)該執(zhí)行全量復(fù)制還是部分復(fù)制呢?如果執(zhí)行部分復(fù)制,主服務(wù)器又如何補償從服務(wù)器A 在斷線期間丟失的那部分數(shù)據(jù)呢?這些問題的答案都在復(fù)制積壓緩沖區(qū)里面
2、復(fù)制積壓緩沖區(qū)
復(fù)制積壓緩沖區(qū)是保存在主節(jié)點上的一個固定長度的隊列,默認大小為 1MB,當(dāng)主節(jié)點有連接的從節(jié)點(slave)時被創(chuàng)建,這時主節(jié)點(master) 響應(yīng)寫命令時,不但會把命令發(fā)送給從節(jié)點,還會寫入復(fù)制積壓緩沖區(qū),如下圖所示:
因此,主服務(wù)器的復(fù)制積壓緩沖區(qū)里面會保存著一部分最近傳播的寫命令,并且復(fù)制積壓緩沖區(qū)會為隊列中的每個字節(jié)記錄相應(yīng)的復(fù)制偏移量。所以當(dāng)從服務(wù)器重新連上主服務(wù)器時,從服務(wù)器通過 psync 命令將自己的復(fù)制偏移量 offset 發(fā)送給主服務(wù)器,主服務(wù)器會根據(jù)這個復(fù)制偏移量來決定對從服務(wù)器執(zhí)行何種數(shù)據(jù)同步操作:
3、服務(wù)器運行ID
每個 Redis 節(jié)點啟動后都會動態(tài)分配一個 40 位的十六進制字符串作為運行 ID,運行 ID 的主要作用是用來唯一識別 Redis 節(jié)點,我們可以使用?info server
命令來查看
127.0.0.1:6379> info server
這里面有一個run_id
字段就是服務(wù)器運行的ID
了解這幾個概念之后,我們一起來看看 psync 命令的運行流程,psync 命令運行流程如下圖所示:
psync 命令的邏輯比較簡單,整個流程分為兩步:
1、從節(jié)點發(fā)送 psync 命令給主節(jié)點,參數(shù) runId 是當(dāng)前從節(jié)點保存的主節(jié)點運行ID,參數(shù)offset是當(dāng)前從節(jié)點保存的復(fù)制偏移量,如果是第一次參與復(fù)制則默認值為 -1。
2、主節(jié)點接收到 psync 命令之后,會向從服務(wù)器返回以下三種回復(fù)中的一種:
當(dāng)主節(jié)點把當(dāng)前的數(shù)據(jù)同步給從節(jié)點后,便完成了復(fù)制的建立流程。但是主從服務(wù)器并不會斷開連接,因為接下來主節(jié)點會持續(xù)地把寫命令發(fā)送給從節(jié)點,保證主從數(shù)據(jù)一致性。
經(jīng)過上面 7 步就完成了主從服務(wù)器之間的數(shù)據(jù)同步,由于這篇文章的篇幅比較長,關(guān)于全量復(fù)制和部分復(fù)制的細節(jié)就不介紹了,全量復(fù)制就是將主節(jié)點的當(dāng)前的數(shù)據(jù)生產(chǎn) RDB 文件,發(fā)送給從服務(wù)器,從服務(wù)器再從本地磁盤加載,這樣當(dāng)文件過大時就需要特別大的網(wǎng)絡(luò)開銷,不然由于數(shù)據(jù)傳輸比較慢會導(dǎo)致主從數(shù)據(jù)延時較大,部分復(fù)制就是主服務(wù)器將復(fù)制積壓緩沖區(qū)的寫命令直接發(fā)送給從服務(wù)器。
心跳檢測是發(fā)生在主從節(jié)點在建立復(fù)制后,它們之間維護著長連接并彼此發(fā)送心跳命令,便以后續(xù)持續(xù)發(fā)送寫命令,主從心跳檢測如下圖所示:
主從節(jié)點彼此都有心跳檢測機制,各自模擬成對方的客戶端進行通信,主從心跳檢測的規(guī)則如下:
主節(jié)點根據(jù) replconf 命令判斷從節(jié)點超時時間,體現(xiàn)在 info replication 統(tǒng) 計中的 lag 信息中,我們在主服務(wù)器上執(zhí)行 info replication 命令:
127.0.0.1:6379> info replication
可以看出 slave0 字段的值最后面有一個 lag,lag 表示與從節(jié)點最后一次通信延遲的秒數(shù),正常延遲應(yīng)該在 0 和 1 之間。如果超過 repl-timeout 配置的值(默認60秒),則判定從節(jié)點下線并斷開復(fù)制客戶端連接,如果從節(jié)點重新恢復(fù),心跳檢測會繼續(xù)進行。
Redis 的主從拓撲結(jié)構(gòu)可以支持單層或多層復(fù)制關(guān)系,根據(jù)拓撲復(fù)雜性可以分為以下三種:一主一從、一主多從、樹狀主從架構(gòu)
一主一從結(jié)構(gòu)是最簡單的復(fù)制拓撲結(jié)構(gòu),我們前面搭建的就是一主一從的架構(gòu),架構(gòu)如圖所示:
一主一從架構(gòu)用于主節(jié)點出現(xiàn)宕機時從節(jié)點 提供故障轉(zhuǎn)移支持,當(dāng)應(yīng)用寫命令并發(fā)量較高且需要持久化時,可以只在從節(jié)點上開啟 AOF,這樣既保證數(shù)據(jù)安全性同時也避免了持久化對主節(jié)點的性能干擾。但是這里有一個坑,需要你注意,就是當(dāng)主節(jié)點關(guān)閉持久化功能時, 如果主節(jié)點脫機要避免自動重啟操作。因為主節(jié)點之前沒有開啟持久化功能自動重啟后數(shù)據(jù)集為空,這時從節(jié)點如果繼續(xù)復(fù)制主節(jié)點會導(dǎo)致從節(jié)點數(shù)據(jù)也被清空的情況,喪失了持久化的意義。安全的做法是在從節(jié)點上執(zhí)行 slaveof no one 斷開與主節(jié)點的復(fù)制關(guān)系,再重啟主節(jié)點從而避免這一問題
一主多從架構(gòu)又稱為星形拓撲結(jié)構(gòu),一主多從架構(gòu)如下圖所示:
一主多從架構(gòu)可以實現(xiàn)讀寫分離來減輕主服務(wù)器的壓力,對于讀占比較大的場景,可以把讀命令發(fā)送到 從節(jié)點來分擔(dān)主節(jié)點壓力。同時在日常開發(fā)中如果需要執(zhí)行一些比較耗時的讀命令,如:keys、sort等,可以在其中一臺從節(jié)點上執(zhí)行,防止慢查詢對主節(jié)點造成阻塞從而影響線上服務(wù)的穩(wěn)定性。對于寫并發(fā)量較高的場景,多個從節(jié)點會導(dǎo)致主節(jié)點寫命令的多次發(fā)送從而過度消耗網(wǎng)絡(luò)帶寬,同時也加重了主節(jié)點的負載影響服務(wù)穩(wěn)定性。
樹狀主從架構(gòu)又稱為樹狀拓撲架構(gòu),樹狀主從架構(gòu)如下圖所示:
樹狀主從架構(gòu)使得從節(jié)點不但可以復(fù)制主節(jié) 數(shù)據(jù),同時可以作為其他從節(jié)點的主節(jié)點繼續(xù)向下層復(fù)制。解決了一主多從架構(gòu)中的不足,通過引入復(fù)制中 間層,可以有效降低主節(jié)點負載和需要傳送給從節(jié)點的數(shù)據(jù)量。如架構(gòu)圖中,數(shù)據(jù)寫入節(jié)點A 后會同步到 B 和 C節(jié)點,B節(jié)點再把數(shù)據(jù)同步到 D 和 E節(jié)點,數(shù)據(jù)實現(xiàn)了一層一層的向下復(fù)制。當(dāng)主節(jié)點需要掛載多個從節(jié)點時為了避免對主節(jié)點的性能干擾,可以采用樹狀主從結(jié)構(gòu)降低主節(jié)點壓力。
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準確進行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。