共計 4365 個字符,預計需要花費 11 分鐘才能閱讀完成。
丸趣 TV 小編給大家分享一下 Redis 中主從同步機制的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
之前的文章中詳細分析了 redis 的特性和核心原理,從本篇開始將對 redis 的部署結構和運行模式進行分析解讀。真正生產(chǎn)環(huán)境當中我們基本不會使用單節(jié)點的 redis 來提供服務,至少是主從結構的哨兵或者集群模式,以保障 redis 服務的可靠性。本篇就來詳細解讀下 redis 的主從同步機制。
一、Redis 主從有兩種結構模型:
1.1 主從復制
一主 N 從的這種復制結構復制關系只有一級,也是使用最多的形式,通常搭建哨兵或者集群結構的 redis 都是采用的這種復制結構,能夠通過一級從節(jié)點的復制關系很好的保證服務的可用性,做到異常情況主從切換。
1.2 級聯(lián)復制
級聯(lián)復制結構的復制關系可以有多級,一個主節(jié)點的從節(jié)點可以是下屬從節(jié)點的主節(jié)點。級聯(lián)復制結構的應用相對比較少,這種結構能夠在有多個從節(jié)點的結構下一定程度上緩解主節(jié)點的復制壓力。
二、Redis 主從關系的建立
redis 的主從同步始于命令 SLAVEOF host port,通過這個命令能夠建立主從關系,SLAVEOF 命令用于在 Redis 運行時動態(tài)地修改復制功能的行為。通過執(zhí)行 SLAVEOF host port 命令,可以將當前服務器轉(zhuǎn)變?yōu)橹付ǚ掌鞯膹膶俜掌?(slave server)。如果當前服務器已經(jīng)是某個主服務器(master server) 的從屬服務器,那么執(zhí)行 SLAVEOF host port 將使當前服務器停止對舊主服務器的同步,丟棄舊數(shù)據(jù)集,轉(zhuǎn)而開始對新主服務器進行同步。另外,對一個從屬服務器執(zhí)行命令 SLAVEOF NO ONE 將使得這個從屬服務器關閉復制功能,并從從屬服務器轉(zhuǎn)變回主服務器,原來同步所得的數(shù)據(jù)集不會被丟棄。利用 SLAVEOF NO ONE 不會丟棄同步所得數(shù)據(jù)集這個特性,在沒有搭建哨兵和集群的情況下,可以在主服務器失敗的時候,將從屬服務器用作新的主服務器,從而實現(xiàn)無間斷運行。
下圖為主從關系建立流程:
注意:
根據(jù)上執(zhí)行流程這里有一個需要注意點,當我們對一個已有主從關系的節(jié)點執(zhí)行 slaveof 命令時,會結束掉現(xiàn)有的主從關系并清空節(jié)點下的所以數(shù)據(jù),在生成環(huán)境當中這是比較威脅的操作。有沒有更安全的方式了?上面介紹 slavelof 命令的時候提到可以傳遞 NO ONE 參數(shù),也就是執(zhí)行 SLAVEOF NO ONE 命令,這個命令是只會結束主從復制關系不會清空數(shù)據(jù)的,相對安全很多。
三、數(shù)據(jù)同步
建立好主從關系后就要進入主從數(shù)據(jù)同步的過程了,這里主要分三種情況,剛建立主從關系后的數(shù)據(jù)全量同步;初始化同步完成后的命令傳播階段;主從關系異常中斷重連后的同步方式選擇,這里會有全量和增量同步兩種場景。
3.1 全量同步
當 slave 節(jié)點啟動后或斷開重連后(重連不滿足增量同步條件),會向 master 數(shù)據(jù)庫發(fā)送 SYNC 命令。
master 節(jié)點收到 SYNC 命令后會開始在后臺保存快照(即 RDB 持久化,在主從復制時,會無條件觸發(fā) RDB),并將保存快照期間接收到的命令緩存起來。
master 節(jié)點執(zhí)行 RDB 持久化完成后,向所有 slave 節(jié)點發(fā)送快照 RDB 文件,并在發(fā)送快照期間繼續(xù)記錄被執(zhí)行的寫命令。
slave 節(jié)點收到快照文件后丟棄所有舊數(shù)據(jù)(會清空所有數(shù)據(jù)),載入收到的快照。
master 節(jié)點快照發(fā)送完畢、slave 節(jié)點載入快照完畢后,master 節(jié)點開始向 slave 節(jié)點發(fā)送緩沖區(qū)中的寫命令。
slave 節(jié)點完成對快照的載入,開始接收命令請求,并執(zhí)行來自主數(shù)據(jù)庫緩沖區(qū)的寫命令。(從數(shù)據(jù)庫初始化完成)
master 節(jié)點每執(zhí)行一個寫命令就會向 slave 節(jié)點發(fā)送相同的寫命令,slave 節(jié)點接收并執(zhí)行收到的寫命令。(命令傳播操作,slave 節(jié)點初始化完成后的操作)
全量同步流程如下圖:
在 redis2.8 之前,從節(jié)點無論是初始化還是斷線重連后都是采用全量同步的方式,在 2.8 之后版本,引入 PSYNC 命令,在從節(jié)點斷線重連后會判斷是否采用增量同步。
3.2 增量同步
PSYNC 具備了數(shù)據(jù)全量重同步和增量同步模式。
全量重同步:跟舊版復制基本是一致的,可以理解為“全量”復制。
部分重同步:salve 斷開又重新連時,在命令傳播階段,只需要發(fā)送與 master 斷開這段時間執(zhí)行的寫命給 slave 即可,可以理解為“增量”復制。
PSYNC 執(zhí)行過程中比較重要的概念有 3 個:runid、offset(復制偏移量)以及復制積壓緩沖區(qū)。
1.runid
每個 Redis 服務器都會有一個表明自己身份的 ID。在 PSYNC 中發(fā)送的這個 ID 是指之前連接的 Master 的 ID,如果沒保存這個 ID,PSYNC 的命令會使用”PSYNC ? -1”這種形式發(fā)送給 Master,表示需要全量復制。
2.offset(復制偏移量)
在主從復制的 Master 和 Slave 雙方都會各自維持一個 offset。Master 成功發(fā)送 N 個字節(jié)的命令后會將 Master 里的 offset 加上 N,Slave 在接收到 N 個字節(jié)命令后同樣會將 Slave 里的 offset 增加 N。Master 和 Slave 如果狀態(tài)是一致的那么它的的 offset 也應該是一致的。
3. 復制積壓緩沖區(qū)
復制積壓緩沖區(qū)是由 Master 維護的一個固定長度環(huán)形積壓隊列(FIFO 隊列),它的作用是緩存已經(jīng)傳播出去的命令。當 Master 進行命令傳播時,不僅將命令發(fā)送給所有 Slave,還會將命令寫入到復制積壓緩沖區(qū)里面。PSYNC 執(zhí)行過程和 SYNC 的區(qū)別在于:salve 連接時,判斷是否需要全量同步,全量同步的邏輯過程和 SYNC 一樣。PSYNC 執(zhí)行步驟如下:
客戶端向服務器發(fā)送 SLAVEOF 命令,即 salve 向 master 發(fā)起連接請求時,slave 根據(jù)自己是否保存 Master runid 來判斷是否是第一次連接。
如果是第一次同步則向 Master 發(fā)送 PSYNC ? -1 命令來進行完整同步;如果是重連接,會向 Master 發(fā)送 PSYNC runid offset 命令(runid 是 master 的身份 ID,offset 是從節(jié)點同步命令的全局遷移量)。
Master 接收到 PSYNC 命令后,首先判斷 runid 是否和本機的 id 一致,如果一致則會再次判斷 offset 偏移量和本機的偏移量相差有沒有超過復制積壓緩沖區(qū)大小,如果沒有那么就給 Slave 發(fā)送 CONTINUE,此時 Slave 只需要等待 Master 傳回失去連接期間丟失的命令。如果 runid 和本機 id 不一致或者 offset 差距超過了復制積壓緩沖區(qū)大小,那么就會返回 FULLRESYNC runid offset,Slave 將 runid 保存起來,并進行全量同步。
主節(jié)點在命令傳播時,主數(shù)據(jù)庫會將每一個寫命令傳遞給從數(shù)據(jù)庫的同時,都會將寫命令存放到積壓隊列,并記錄當前積壓隊列中存放命令的全局偏移量 offset。當 salve 重連接時,master 會根據(jù)從節(jié)點傳的 offset 在環(huán)形積壓隊列中找到斷開這段時間執(zhí)行的命令,并同步給 salve 節(jié)點,達到增量同步結果。
PSYNC 執(zhí)行流程如下圖:
從以上 PSYNC 的執(zhí)行流程可以看出當 slave 節(jié)點斷線重連以后判斷是否采用增量同步的核心是 slave 的 offset 偏移量和 master 的偏移量相差有沒有超過復制積壓緩沖區(qū)大小,那么這個大小是由以下參數(shù)來配置的。復制積壓緩沖區(qū)本質(zhì)上是一個固定長度的循環(huán)隊列,默認情況下積壓隊列的大小為 1MB,可以通過配置文件設置隊列大小:設置復制積壓緩沖區(qū)大小,積壓隊列越大,允許主從數(shù)據(jù)庫斷線的時間就越長
repl-backlog-size 1mb
Redis 同時也提供了當沒有 slave 需要同步的時候,多久可以釋放環(huán)形隊列,默認一小時,沒有 salve 連接時,多久釋放一次復制積壓緩沖區(qū)
repl-backlog-ttl 3600
四、主從復制策略
Redis 采用了樂觀復制的策略,也就是在一定程度內(nèi)容忍主從數(shù)據(jù)庫的內(nèi)容不一致,但是保持主從數(shù)據(jù)庫數(shù)據(jù)的最終一致性。具體來說,Redis 在主從復制的過程中,本身就是異步的,在主從數(shù)據(jù)庫執(zhí)行完客戶端請求后會立即將結果返回給客戶端,并異步的將命令同步給從數(shù)據(jù)庫,但是這里并不會等待從數(shù)據(jù)庫完全同步之后,再返回客戶端。這一特性雖然保證了主從復制期間性能不受影響,但是也會產(chǎn)生一個數(shù)據(jù)不一致的時間窗口,如果在這個時間窗口期間網(wǎng)絡突然斷開連接,就會導致兩者數(shù)據(jù)不一致。如果不在配置文件中添加其他策略,那就默認會采用這種方式。為了防止主從不一致不可控,redis 提供了以下兩個參數(shù)來做約束:
min-slaves-to-write 3
min-slaves-max-lag 10
當 slave 數(shù)量小于 min-slaves-to-write,且延遲小于等于 min-slaves-max-lag 時,master 停止寫入操作。
還有一個參數(shù)也會影響主從之間的延時:
repl-disable-tcp-nodelay:
設置成 yes,則 redis 會合并小的 TCP 包從而節(jié)省帶寬,但會增加同步延遲,造成 master 與 slave 數(shù)據(jù)不一致。設置成 no,則 redis master 會立即發(fā)送同步數(shù)據(jù),幾乎沒有延遲。
Redis 的主從同步無論那種場景可以抽象為以下七個步驟:
1. 建立 socket 連接
從服務器根據(jù)設置的套接字創(chuàng)建連向主服務器的套接字連接,主服務器接收從服務器的套接字連接之后,為該套接字創(chuàng)建響應的客戶端狀態(tài),并將此時的從服務器看做是主服務器的客戶端,也就是該從服務器同時具備服務器與客戶端兩個身份。
2. 發(fā)送 PING 命令
PING 命令主要有兩種作用:雖然建立了套接字連接,但是還未使用過,通過發(fā)送 PING 命令檢查套接字的讀寫狀態(tài)是否正常;通過發(fā)送 PING 命令檢查主服務器能否正常處理命令請求,能處理主服務器回復 PONG。
3. 身份驗證
從服務器接收到主服務器返回的“PONG”回復,接下來就需要考慮身份驗證的事。如果從服務器設置了 masterauth 選項,那么進行身份驗證,如果從服務器沒有設置 masterauth 選項,那么不進行身份驗證。
4. 發(fā)送端口信息
在身份驗證步驟之后,從服務器將執(zhí)行命令 REPLCONF listening-port,向主服務器發(fā)送從服務器的監(jiān)聽端口號。
5. 數(shù)據(jù)同步
從服務器向主服務器發(fā)送 SYNC 命令、PSYNC 命令,執(zhí)行同步操作。
6. 命令傳播
主從服務器就會進入命令傳播階段,主服務器只要將自己執(zhí)行的寫命令發(fā)送給從服務器,而從服務器只要一直執(zhí)行并接收主服務器發(fā)來的寫命令。
以上是“Redis 中主從同步機制的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注丸趣 TV 行業(yè)資訊頻道!