前言
只有光頭才能變強
好的,今天我們要上鉑金段位了,如果還沒經歷過青銅和白銀和黃金階段的,可以先去蹭蹭經驗再回來:
這篇文章主要講的是Redis主從複製。因為Redis叢集的知識點有點多,所以鉑金上分得要好幾篇~
文字力求簡單講清每個知識點,希望大家看完能有所收穫
一、主從架構
1.1為什麼要主從架構
Redis也跟關係型資料(MySQL)一樣,如果有過多請求還是撐不住的。
因為Redis如果只有一臺伺服器的話,那隨著請求越來越多:
- Redis的記憶體是有限的,可能放不下那麼多的資料
- 單臺Redis支援的併發量也是有限的。
- 萬一這臺Redis掛了,所有的請求全走關聯式資料庫了,那就更炸了。
顯然,出現的上述問題是因為一臺Redis伺服器不夠,所以多搞幾臺Redis伺服器就可以了
為了實現我們服務的高可用性,可以將這幾臺Redis伺服器做成是主從來進行管理
tip:Redis作者已將Master/Slave架構改名為Master/Replica
1.2主從架構的特點
下面我們來看看Redis的主從架構特點:
- 主伺服器負責接收寫請求
- 從伺服器負責接收讀請求
- 從伺服器的資料由主伺服器複製過去。主從伺服器的資料是一致的
主從架構的好處:
- 讀寫分離(主伺服器負責寫,從伺服器負責讀)
- 高可用(某一臺從伺服器掛了,其他從伺服器還能繼續接收請求,不影響服務)
- 處理更多的併發量(每臺從伺服器都可以接收讀請求,讀QPS就上去了)
主從架構除了上面的形式,也有下面這種的(只不過用得比較少):
二、複製功能
主從架構的特點之一:主伺服器和從伺服器的資料是一致的。
因為主伺服器是能接收寫請求的,主伺服器處理完寫請求,會做什麼來保證主從資料的一致性呢?如果主從伺服器斷開了,過一陣子才重連,又會怎麼處理呢?下面將會了解到這些細節~
在Redis中,使用者可以通過執行SALVEOF命令或者設定salveof選項,讓一個伺服器去複製(replicate)另一個伺服器,我們稱呼被複制的伺服器為主伺服器(master),而對主伺服器進行復制的伺服器則被稱為從伺服器(salve)
2.1複製功能的具體實現
複製功能分為兩個操作:
- 同步(sync)
- 將從伺服器的資料庫狀態更新至主伺服器的資料庫狀態
- 命令傳播(command propagate)
- 主伺服器的資料庫狀態被修改,導致主從伺服器的資料庫狀態不一致,讓主從伺服器的資料庫狀態重新回到一致狀態。
從伺服器對主伺服器的同步又可以分為兩種情況:
- 初次同步:從伺服器沒有複製過任何的主伺服器,或者從伺服器要複製的主伺服器跟上次複製的主伺服器不一樣。
- 斷線後同步:處於命令傳播階段的主從伺服器因為網路原因中斷了複製,從伺服器通過自動重連重新連線主伺服器,並繼續複製主伺服器
在Redis2.8以前,斷線後複製這部分其實缺少的只是部分的資料,但是要讓主從伺服器重新執行SYNC命令,這樣的做法是非常低效的。(因為執行SYNC命令是把所有的資料再次同步,而不是隻同步丟失的資料)
接下來我們來詳細看看Redis2.8以後複製功能是怎麼實現的:
2.1.1複製的前置工作
首先我們來看一下前置的工作:
- 從伺服器設定主伺服器的IP和埠
- 建立與主伺服器的Socket連線
- 傳送PING命令(檢測Socket讀寫是否正常與主伺服器的通訊狀況)
- 身份驗證(看有沒有設定對應的驗證配置)
- 從伺服器給主伺服器傳送埠的資訊,主伺服器記錄監聽的埠
前面也提到了,Redis2.8之前,斷線後同步會重新執行SYNC命令,這是非常低效的。下面我們來看一下Redis2.8之後是怎麼進行同步的。
Redis從2.8版本開始,使用PSYNC命令來替代SYNC命令執行復制時同步的操作。
PSYNC命令具有完整重同步和部分重同步兩種模式(其實就跟上面所說的初次複製和斷線後複製差不多個意思)。
2.1.2完整重同步
下面先來看看完整重同步是怎麼實現的:
- 從伺服器向主伺服器傳送PSYNC命令
- 收到PSYNC命令的主伺服器執行BGSAVE命令,在後臺生成一個RDB檔案。並用一個緩衝區來記錄從現在開始執行的所有寫命令。
- 當主伺服器的BGSAVE命令執行完後,將生成的RDB檔案傳送給從伺服器,從伺服器接收和載入RBD檔案。將自己的資料庫狀態更新至與主伺服器執行BGSAVE命令時的狀態。
- 主伺服器將所有緩衝區的寫命令傳送給從伺服器,從伺服器執行這些寫命令,達到資料最終一致性。
2.1.2部分重同步
接下來我們來看看部分重同步,部分重同步可以讓我們斷線後重連只需要同步缺失的資料(而不是Redis2.8之前的同步全部資料),這是符合邏輯的!
部分重同步功能由以下部分組成:
- 主從伺服器的複製偏移量
- 主伺服器的複製積壓緩衝區
- 伺服器執行的ID(run ID)
首先我們來解釋一下上面的名詞:
複製偏移量:執行復制的雙方都會分別維護一個複製偏移量
- 主伺服器每次傳播N個位元組,就將自己的複製偏移量加上N
- 從伺服器每次收到主伺服器的N個位元組,就將自己的複製偏移量加上N
通過對比主從複製的偏移量,就很容易知道主從伺服器的資料是否處於一致性的狀態!
那斷線重連以後,從伺服器向主伺服器傳送PSYNC命令,報告現在的偏移量是36,那麼主伺服器該對從伺服器執行完整重同步還是部分重同步呢??這就交由複製積壓緩衝區來決定。
當主伺服器進行命令傳播時,不僅僅會將寫命令傳送給所有的從伺服器,還會將寫命令入隊到複製積壓緩衝區裡面(這個大小可以調的)。如果複製積壓緩衝區存在丟失的偏移量的資料,那就執行部分重同步,否則執行完整重同步。
伺服器執行的ID(run ID)實際上就是用來比對ID是否相同。如果不相同,則說明從伺服器斷線之前複製的主伺服器和當前連線的主伺服器是兩臺伺服器,這就會進行完整重同步。
所以流程大概如此:
2.1.3命令傳播
當完成了同步之後,主從伺服器就會進入命令傳播階段。這時主伺服器只要將自己的寫命令傳送給從伺服器,而從伺服器接收並執行主伺服器傳送過來的寫命令,就可以保證主從伺服器一直保持資料一致了!
在命令傳播階段,從伺服器預設會以每秒一次的頻率,向伺服器傳送命令REPLCONF ACK <replication_offset>
其中replication_offset是從伺服器當前的複製偏移量
傳送這個命令主要有三個作用:
- 檢測主從伺服器的網路狀態
- 輔助實現min-slaves選項
- 檢測命令丟失
五、最後
畫了好久好久的圖,終於寫完啦。
拋個問題:如果從伺服器掛了,沒關係,我們一般會有多個從伺服器,其他的請求可以交由沒有掛的從伺服器繼續處理。如果主伺服器掛了,怎麼辦?因為我們的寫請求由主伺服器處理,只有一臺主伺服器,那就無法處理寫請求了?
問題留到下篇解決~~
參考資料:
- 《Redis設計與實現》
- 《Redis實戰》
如果你覺得我寫得還不錯,瞭解一下:
- 堅持原創的技術公眾號:Java3y。
- 文章的目錄導航(精美腦圖+海量視訊資源):github.com/ZhongFuChen…