單臺 redis 面臨的問題
在實際場景中,單一節點的 redis 容易面臨風險
-
機器故障
- 我們部署到一臺 redis 伺服器,當發生機器故障時,需要遷移到另一臺伺服器並且要保證資料是同步的,而資料是最重要的。單臺機器無法保證資料的安全
-
容量瓶勁
- 當我們有需求需要擴容 redis 記憶體時,從16G 記憶體升級到 64G,單機肯定是滿足不了,除非重新買一個128G的機器
-
總結
- 要實現分散式資料庫更大的儲存容量和承受高併發訪問量,我們會將原來集中式資料庫的資料分別儲存到其他多個網路節點上
- Redis 為了解決這個單一節點的問題,也會把資料複製多個副本部署到其他節點上進行復制,實現 Redis 的高可用,實現對資料的冗餘備份,從而保證資料和服務的高可用
什麼是主從複製
- 主從複製,是指將一臺 Redis 伺服器的資料,複製到其他的 Redis 伺服器,前者成為 master 主節點,後者成為 slave 從節點,資料的複製是單向的,只能由主節點到從節點。
- 預設情況下,每臺 redis 伺服器都是主節點,且一個主節點可以有多個從節點,但一個從節點只能有一個主節點
主從複製的作用
資料冗餘
- 主從複製實現了資料的熱備份,是持久化之外的另一種資料冗餘方式
故障恢復
- 當主節點出現問題時,可以由從節點提供服務,實現快速的故障恢復,實際上是一種服務的冗餘
負載均衡
- 在主從複製的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務。「即寫 Redis 資料時應用連線主節點,讀 Redis 資料時應用連線從節點」分擔伺服器負載,尤其是在 「寫少讀多」 的場景下,透過多個從節點分擔讀負載,可以大大提高 Redis 伺服器的併發量
讀寫分離
- 可以用於實現讀寫分離,主庫寫,從庫讀,讀寫分離不僅可以提高伺服器的負載能力,同時可根據需求的變化,改變從庫的數量
高可用的基石
- 除了上述的作用外,主從複製還是哨兵和叢集能夠實施的基礎,因此說主從複製是 Redis 高可用的基石
主從複製啟用
從節點開啟主從複製,有3中方式
-
配置檔案
- 在從伺服器的配置檔案中加入: slaveof
-
啟動命令
- redis-server 啟動命令後加上 --slaveof
-
客戶端命令
- Redis 伺服器啟動後,直接透過客戶端執行命令 slaveof 則該Redis例項成為從節點
檢視複製資訊
- 透過 info replication 命令可以看到複製的一些資訊
配置檔案中注意事項
- 檢視 LOG 檔案位置
- 保護模式
- 密碼配置
主從複製原理
-
主從複製大體分為3個步驟:
1.連線建立階段「準備階段」
2.資料同步階段
3.命令傳播階段
-
從節點執行 slaveof 命令後,會進行以下步驟進行復制
-
主從配置資訊之後的日誌記錄也可以看出以上流程
- 儲存主節點資訊
-
執行 slaveof 後,redis 會列印以下日誌資訊\
-
主從節點建立 socket 連線
-
從節點透過內部執行定時任務,維護複製相關邏輯,當定時任務發現新的主節點後,會嘗試與該節點進行網路連線
-
從節點與主節點建立網路連線\
-
從節點會建立一個 socket 套接字,然後建立一個埠為 51234 的套接字,專門使用者接收主節點傳送的複製命令,從節點建立連線後的日誌\
-
如果從節點無法建立連線,定時任務會無限重試直到連線成功或者執行 slaveof no one 取消複製
-
關於連線失敗
- 在從節點執行 info replication 命令檢視 master_link_down_since_seconds 這個指標,它會記錄與主節點連線失敗的系統時間
- 從節點連線主節點失敗時,也會在日誌中每秒列印失敗資訊 # Error condition on socket for SYNC: {socket_error_reason}
-
-
-
傳送Ping 命令
-
建立連線成功後,從節點傳送 Ping 請求進行首次通訊,Ping 命令的主要目的
- 檢測主從之間網路套接字是否可用
- 檢測主節點當前是否可以接收命令
- 如果傳送 Ping 命令之後,從節點沒有收到主節點的 Pong 回覆或者網路超時,比如網路超時或者主節點阻塞無法響應命令,從節點會斷開復制連線,下次定時任務會發起重連
\
- 從節點傳送的 Ping 命令成功返回後,Redis 列印日誌,並繼續後續的複製流程
-
-
許可權驗證
- 如果主節點設定了 requirepass 引數,則需要密碼驗證,從節點必須配置 masterauth 引數保證與主節點相同的密碼才能驗證透過,如果驗證失敗,複製將終止,從節點重新發起復制流程
-
同步資料集
- 主從複製連線正常通訊後,對於首次建立的複製場景,主節點會把所有的資料全部傳送給從節點,這步是耗時最長的步驟
-
命令持續複製
- 當主節點把當前資料全部傳送到從節點後,便完成了複製流程,接下來主節點會持續的把命令傳送給從節點,保證主從資料的一致性
-
全量複製和部分複製
全量複製
-
用於初次複製或者其他無法進行部分複製的情況,將主節點的所有資料都傳送給從節點,是一個非常重型的操作,當資料量較大時,會對主從節點和網路造成很大的開銷
-
全量複製過程
- redis-slave 內部會發出一個同步命令,剛開始是 Psync 命令 , Psync?-1 表示請求 master 主節點同步資料
- 主機向從節點傳送 runid 和 offset ,因為從節點並沒有 對應 的 offset ,所以是全量複製
- 從節點會儲存主機的相關資訊 save masterinfo
- 主機接收到複製命令後,執行 bgsave「非同步執行」在後臺生成 RDB 快照檔案,並使用一個緩衝區「複製緩衝區」來記錄現在開始執行的所有寫命令
- 主機 send RDB 傳送 RDB 快照檔案給從機
- 傳送緩衝區資料
- 重新整理舊的資料,從節點在接收主節點資料之前將之前原有的老資料清空
- 載入 RDB 快照檔案,將資料庫狀態更新直主機執行 gbsave 時的資料庫狀態和緩衝區資料的載入
-
全量複製的開銷
- bgsave 的時間
- RDB 檔案網路傳輸時間
- 從節點清空資料的時間
- 從節點載入 RDB 檔案的時間
部分複製
-
部分複製是 Redis 2.8 以後出現的,之所以要加上部分複製,是因為全量複製會出現很多的問題,比如開銷時間大,無法隔離等問題。\
redis 希望在網路抖動的時候,可以有一些機制將複製的損失降到最低; -
用於處理在主從複製中因網路閃斷等原因造成的資料丟失場景,當從節點再次連上主節點後,如果條件允許,主節點會補發丟失資料給從節點,因為補發的資料遠遠小於全量資料,可以有效避免全量複製的過高開銷,需要注意的是,如果網路中斷時間過長,造成主節點沒有能夠完整的儲存中斷期間執行的寫命令,則無法進行部分複製,仍使用全量複製
-
部分複製的過程
- 如果網路抖動(connection lost)
- Redis 主機還是會寫 replbackbuffer「複製積壓緩衝區」
- 從機 slave 會繼續連線從機
- 從機把自己當前的 runid 和 offset 傳送給主機,並且執行 Pysnc 執行同步
- 如果 master 主機發現偏移量在複製積壓緩衝區的範圍內,就會返回 continue 命令,繼續執行同步
- 同步了 offset 的資料,所以部分複製是基於 offset 「偏移量」的
redis 是如何決定使用全量複製或部分複製的?
-
redis 選擇決策
- 從節點將 offset 傳送給主節點後,主節點根據偏移量和複製緩衝區大小來決定是否執行部分複製
- 如果offset 偏移量之後的資料,仍然在複製積壓緩衝區的話,使用部分複製
- 如果 offset 偏移量之後的資料,已經不在積壓緩衝區的話,說明資料已經被擠出,所以進行全量複製
-
如何避免全量複製?
-
調整積壓緩衝區的大小
- 由於緩衝區大小固定且有限,因此可以備份的資料也有限,當主從節點的 offset 差距過大超過積壓緩衝區時,無法進行部分複製,只能使用全量複製;
- 反過來說,為了提高網路中斷時部分複製的使用機率,可以根據需要增大複製緩衝區的大小來調節「透過配置 repl_backlog_size」來設定。
- 例如:如果網路中斷的平均時間是 60S,而主節點平均每秒執行的寫命令是 100KB,則複製積壓緩衝區的平均需求為 6MB, 為了保險期間,設定成 12MB來保證絕大多數斷線情況下可以使用部分複製
-
伺服器執行ID「runid」
- 每個redis 機器「無論主從」在啟動時都會隨機生成一個隨機的ID「每次啟動都不一樣」,由40個隨機的十六進位制的字元組成;runid 用來唯一表示 redis 節點,透過 info server 命令檢視 runid
- 主節點初次複製時,主節點將自己的runid 傳送給從節點,從節點把這個 runid 儲存起來,當斷線重連時,從節點將這個 runid 傳送給主節點,主節點用這個 runid 來判斷是否可以進行部分複製
- 如果從節點傳送的runid 和 主節點的runid 相同,說明主從節點之前通不過,主節點會嘗試繼續使用部分複製「真正決定使用部分複製的還是offset 和複製積壓緩衝區的大小」
- 如果從節點傳送「儲存」的 runid 和現在主節點的 runid 不同,說明之前主從沒有同步過資料,只能進行全量複製
-
複製偏移量
-
參與複製中的從節點都會維護自身複製偏移量。
-
主節點
- 主節點在處理完寫入命令後,會把命令的位元組長度做累加記錄,統計資訊在 info replication 中的 master_repl_offset 指標中
-
從節點
- 從節點每秒鐘上報自身的複製偏移量給主節點,因此主節點也會儲存從節點的複製偏移量
- 從節點在接收到主節點傳送的命令後,也會累加記錄自身的偏移量。統計資訊在 info replication 中的 slave_repl_offset 中
複製積壓緩衝區
- 複製積壓緩衝區:是儲存在主節點上的一個固定長度的佇列,預設大小是 1MB,當主節點有連線的從節點時被建立,這時主節點響應寫命令時,不但會把命令傳送給從節點,還會寫入複製積壓緩衝區
- 命令傳播階段,主節點除了將寫命令傳送給從節點,還會傳送一份給複製積壓緩衝區,作為寫命令的備份;
- 除了儲存寫命令,複製積壓緩衝區中還儲存了其中的每個位元組對應的複製偏移量「offset」。
- 由於複製積壓緩衝區定長,並且先進先出。所以它儲存的是主節點最近執行的寫命令,時間較早的寫命令會被擠出緩衝區
主從複製的常用相關配置
從資料庫配置
-
1. slaveof
slave 節點需要配置該項,指向 master 的 IP 和 埠
-
2.masterauth
如果 master 啟用了密碼保護,則配置該項需要填寫 master 的啟動密碼,如果master 未啟動該項,則該項需要註釋
-
3. slave-server-stale-data
指定 master 和 slave 連線中斷時的動作。\
預設是 yes,表示 slave 會繼續應答來自 client的請求,但這些資料可能已經過期「因為連線中斷可能無法進行master同步」;若配置為 no , 則 slave 除正常應答 INFO 和 slaveof 「配置命令」外,其餘來自客戶端的請求命令均會得到 “SYNC with master in progress” 的應答,直到該 slave 和master重建連線成功或者 slave 提升為 master
-
4. slave-read-only
指定 slave 是否為只讀,預設為 yes\
若配置為 no ,表示slave 是可寫的,但寫的內容在主從同步以後會被清空 -
5. repl-disable-tcp-nodaly
指定向 slave 同步資料時,是否禁用 socket 的NO_DALY 選項,若配置為 yes ,則表示禁用 NO_DALY ,則 tcp 協議棧會合並小包統一傳送,這樣可以減少主從傳輸間的包數量和減少頻寬,但會增加同步到 slave 的時間;
若配置為 no , 表明啟用 NO_DALY,則 tcp 協議棧 不會延遲小包的傳送時機,這樣資料同步的延時會減少,但需要更大的寬頻;
通常情況下,應該配置為 no 以降低同步的延時,但在主從節點間網路負載很高的情況下,可以配置為 yes
-
6. slave-priority
指定 slave 的優先順序,在不只一個 slave 節點的部署環境下,當 master 當機時,redis-sentinel 會將 priorty 值最小的 slave 提升為 master 。\
需要注意的是,如果該配置項值為 0,則對應的 slave 永遠不會提升為 master
主從複製進階常見問題解決
讀寫分離
-
流量分攤到從節點,這是個非常好的特性,如果一個業務只需要讀資料,那麼可以建立一臺從機進行讀資料操作
-
雖然讀寫有優勢,能夠讓讀這部分分配給各個從節點,如果不夠直接新增 slave 從節點,但是會出現以下問題
-
複製資料延遲
- 可能出現 從節點同步資料不及時,導致資料不一致的問題,當然可以使用監控偏移量,如果 offset 超出偏移量,就切換到 master 機器上,邏輯切換,具體延遲多少可以使用 info replication 命令檢視 offset 指標進行排查
- 對於無法容忍大量延遲的場景,可以編寫外部監控程式,「如consul」監聽主從節點的複製偏移量,當延時較大時出發報警,或通知客戶端,避免讀取延時過高的節點
-
對於N個從節點連線的時候才允許寫入「比較極端的方式」
-
Redis2.8 以後,可以設定主節點只有在N臺從節點連線的時候可以寫入請求。\
然而,因為 redis 使用的是非同步複製,所有沒有辦法保證從節點確實收到的給定的寫入請求,所以存在一個視窗期的資料丟失的可能性。 -
解釋以下這個特性是怎麼工作的
-
從節點每秒都會 Ping 主節點,告知它所有的複製流工作。主節點會記住從每個從節點收到的最新的 Ping
-
使用者可以給主節點配置一個在等於從節點的數量的最低值和不超過最高值之間的延遲,如果至少有N個從節點,如果少於延遲M秒,則寫入將被接受。
-
可能覺得經最大努力保證資料安全的機制,雖然資料一致性無法保證,但是至少只是幾面的時間視窗內丟失資料,通常情況下範圍資料丟失可比無範圍資料丟失好多了
-
如果條件不滿足,主節點將會返回一個錯誤,並且寫入請求將不被接受
-
主節點配置需要兩個引數
- min-slaves-to-write
- min-slaves-max-lag
-
-
-
從節點故障問題
- 對於從節點的故障問題,需要在客戶端維護一個可用從節點列表,當從節點故障時,立即切換到其他從節點或主節點
-
主從配置不一致
-
經常導致主機和從機的配置不同,並帶來問題
-
主機和從機有時候會發生配置不一致的情況,
例如:maxmemory 不一致,如果主機配置 maxmemory 為8G,從機 slave 設定為 4G,這個時候是可以用的,還不會報錯,單數如果要做高可用,讓從節點變成主節點的時候,就會發現資料已經丟失了,而且無法挽回
規避全量複製
-
全量複製指的是,當 slave 從機斷掉並重啟後,runid 產生變化而導致需要在 master 主機裡複製全部資料,這種複製全部資料的過程非常耗資源
-
全量複製是不可避免的。
例如:第一次的全量複製是不可避免的,這時需要選擇小主節點,且 maxmemory 值不要過大,這樣就會比較快,同時選擇在低峰值的時候做全量複製
-
造成全量複製的原因
- 主從機的執行 runid 不匹配。
主節點如果重啟,runid 將會發生變化,如果從節點監控到 runid 是不同一個,它就會認為你的節點不安全,當發生故障轉移的時候,如果主節點發生故障,那麼從機就會變成主節點。
- 複製緩衝區空間不足。
比如預設值:1M 可以部分複製,但如果緩衝區不夠大的話,首先需要網路中斷,部分複製就無法滿足,其次需要增大複製緩衝區配置「relbacklogsize」,對網路的緩衝增強
解決方案:
- 在一些場景下,可能希望對主節點進行重啟。
例如:主節點記憶體碎片率過高,或者希望調整一些只能在啟動時調整的引數,如果使用普通的手段重啟主節點,會使得 runid 發生變化,可能導致不必要的全量複製
- 為了解決這個問題,redis 提供了 debug reload 的重啟的方式,重啟後,主節點的 runid 和 offset 都不受影響,避免了 全量複製
3.當一個主機下面掛了很多個 slave 從機的時候,主機master 掛了,這時 master 主機重啟後,因為 runid 發生了變化,所有的 slave 從機都要做一次全量複製,這將引起但節點和但機器的複製風暴,開銷會非常大
-
解決方案:
- 一般使用一主多從,因為主節點還同時擔任寫操作
- 可以採用樹狀結構降低多個從節點對主節點的消耗
-
從節點採用樹狀結構非常有用,網路開銷給位於中間層的從節點,而不必消耗頂層的主節點,但是這種樹狀結構也帶來了運維的複雜性,增加了手動和自動處理故障轉移的難度\
規避複製風暴
-
解決方案:
- 由於 redis 的單執行緒架構,通常單臺機器會部署多個 redis 例項,當一臺機器上同時部署多個主節點「master」時,如果每個 master 主機只有一臺 slave 從機,那麼當機器當機以後,會產生大量的全量複製,這種情況是非常危險的,頻寬馬上被佔用,會導致不可用
- 應該把主節點儘量分散在多臺機器上,避免在單臺機器上部署過多的主節點。當主節點所在的機器故障後提供故障轉移機制,避免機器恢復後進行密集的全量複製
如何選擇,要不要讀寫分離
沒有最合適的方案,只有最合適的場景,讀寫分離需要業務可以容忍一定程度的資料不一致,適合讀多寫少的業務場景,讀寫分離,是為了要建立一主多從的架構,才能橫向任意擴充套件 slave node 去支撐更大的讀吞吐量
本作品採用《CC 協議》,轉載必須註明作者和本文連結