Redis的主從同步 - 邱乘屹的個人技術博客

redis主從原理

一、複製過程

1、從節點執行 slaveof 命令

2、從節點只是保存了 slaveof 命令中主節點的信息,並沒有立即發起複製

3、從節點內部的定時任務發現有主節點的信息,開始使用 socket 連接主節點

4、連接建立成功後,發送 ping 命令,希望得到 pong 命令響應,否則會進行重連

5、如果主節點設置了權限,那麼就需要進行權限驗證;如果驗證失敗,複製終止。

6、權限驗證通過後,進行數據同步,這是耗時最長的操作,主節點將把所有的數據全部發送給從節點。

7、當主節點把當前的數據同步給從節點後,便完成了複製的建立流程。接下來,主節點就會持續的把寫命令發送給從節點,保證主從數據一致性。

二、數據間的同步

redis 同步有 2 個命令:
sync 和 psync,前者是 redis 2.8 之前的同步命令,後者是 redis 2.8 爲了優化 sync 新設計的命令。我們會重點關注 2.8 的 psync 命令。

psync 命令需要 3 個組件支持:

a、主從節點各自複製偏移量

b、主節點複製積壓緩衝區

c、主節點運行 ID

主從節點各自複製偏移量:

1、參與複製的主從節點都會維護自身的複製偏移量。

2、主節點在處理完寫入命令後,會把命令的字節長度做累加記錄,統計信息在 info replication 中的 masterreploffset 指標中。

3、從節點每秒鐘上報自身的的複製偏移量給主節點,因此主節點也會保存從節點的複製偏移量。

4、從節點在接收到主節點發送的命令後,也會累加自身的偏移量,統計信息在 info replication 中。

5、通過對比主從節點的複製偏移量,可以判斷主從節點數據是否一致。

主節點複製積壓緩衝區:

1、複製積壓緩衝區是一個保存在主節點的一個固定長度的先進先出的隊列。默認大小 1MB。

2、這個隊列在 slave 連接時創建。這時主節點響應寫命令時,不但會把命令發送給從節點,也會寫入複製緩衝區。

3、他的作用就是用於部分複製和複製命令丟失的數據補救。通過 info replication 可以看到相關信息。

主節點運行 ID:

1、每個 redis 啓動的時候,都會生成一個 40 位的運行 ID。

2、運行 ID 的主要作用是用來識別 Redis 節點。如果使用 ip+port 的方式,那麼如果主節點重啓修改了 RDB/AOF 數據,從節點再基於偏移量進行復制將是不安全的。所以,當運行 id 變化後,從節點將進行全量複製。也就是說,redis 重啓後,默認從節點會進行全量複製。

如果在重啓時不改變運行 ID 呢?

可以通過 debug reload 命令重新加載 RDB 並保持運行 ID 不變。從而有效的避免不必要的全量複製。

2、他的缺點則是:debug reload 命令會阻塞當前 Redis 節點主線程,因此對於大數據量的主節點或者無法容忍阻塞的節點,需要謹慎使用。一般通過故障轉移機制可以解決這個問題。

psync 命令的使用方式:

命令格式爲 psync{runId}{offset}

runId:從節點所複製主節點的運行

id offset:當前從節點已複製的數據偏移量

主節點會根據 runid 和 offset 決定返回結果:

1、如果回覆 +FULLRESYNC {runId} {offset} ,那麼從節點將觸發全量複製流程。

2、如果回覆 +CONTINUE,從節點將觸發部分複製。

3、如果回覆 +ERR,說明主節點不支持 2.8 的 psync 命令,將使用 sync 執行全量複製。

4、到這裏,數據之間的同步就講的差不多了,篇幅還是比較長的。主要是針對 psync 命令相關之間的介紹。

三、全量複製

1、全量複製是 Redis 最早支持的複製方式,也是主從第一次建立複製時必須經歷的的階段。

2、觸發全量複製的命令是 sync 和 psync。

3、之前說過,這兩個命令的分水嶺版本是 2.8,redis 2.8 之前使用 sync 只能執行全量不同,2.8 之後同時支持全量同步和部分同步。

四、部分複製

1、當從節點正在複製主節點時,如果出現網絡閃斷和其他異常,從節點會讓主節點補發丟失的命令數據

2、主節點只需要將複製緩衝區的數據發送到從節點就能夠保證數據的一致性,相比較全量複製,成本小很多。

a、當從節點出現網絡中斷,超過了 repl-timeout 時間,主節點就會中斷複製連接。
b、主節點會將請求的數據寫入到“複製積壓緩衝區”,默認 1MB。

c、當從節點恢復,重新連接上主節點,從節點會將 offset 和主節點 id 發送到主節點

d、主節點校驗後,如果偏移量的數後的數據在緩衝區中,就發送 cuntinue 響應 —— 表示可以進行部分複製

e、主節點將緩衝區的數據發送到從節點,保證主從複製進行正常狀態。

五、心跳

主從節點在建立複製後,他們之間維護着長連接並彼此發送心跳命令。

心跳的關鍵機制如下:

1、主從都有心跳檢測機制,各自模擬成對方的客戶端進行通信,通過 client list 命令查看複製相關客戶端信息,主節點的連接狀態爲 flags = M,從節點的連接狀態是 flags = S。

2、主節點默認每隔 10 秒對從節點發送 ping 命令,可修改配置 repl-ping-slave-period 控制發送頻率。

3、從節點在主線程每隔一秒發送 replconf ack{offset} 命令,給主節點上報自身當前的複製偏移量。

4、主節點收到 replconf 信息後,判斷從節點超時時間,如果超過 repl-timeout 60 秒,則判斷節點下線。
注意:爲了降低主從延遲,一般把 redis 主從節點部署在相同的機房/同城機房,避免網絡延遲帶來的網絡分區造成的心跳中斷等情況。

六、總結

Redis主從同步策略

1、主從剛剛連接的時候,進行全量同步;

2、全同步結束後,進行增量同步。

3、如果有需要,slave 在任何時候都可以發起全量同步。

4、redis 策略是,無論如何,首先會嘗試進行增量同步;

5、不成功,要求從機進行全量同步。

注意點:

1、如果多個Slave斷線了,需要重啓的時候,因爲只要Slave啓動,就會發送sync請求和主機全量同步,

2、當多個同時出現的時候,可能會導致Master IO劇增宕機。

主從複製的特點

  1. 採用異步複製;
  2. 一個主redis可以含有多個從redis;
  3. 每個從redis可以接收來自其他從redis服務器的連接;
  4. 主從複製對於主redis服務器來說是非阻塞的,這意味着當從服務器在進行主從複製同步過程中,主redis仍然可以處理外界的訪問請求;
  5. 主從複製對於從redis服務器來說也是非阻塞的,這意味着,即使從redis在進行主從複製過程中也可以接受外界的查詢請求,只不過這時候從redis返回的是以前老的數據,
    如果你不想這樣,那麼在啓動redis時,可以在配置文件中進行設置,那麼從redis在複製同步過程中來自外界的查詢請求都會返回錯誤給客戶端;(雖然說主從複製過程中
    對於從redis是非阻塞的,但是當從redis從主redis同步過來最新的數據後還需要將新數據加載到內存中,在加載到內存的過程中是阻塞的,在這段時間內的請求將會被阻,
    但是即使對於大數據集,加載到內存的時間也是比較多的);
  6. 主從複製提高了redis服務的擴展性,避免單個redis服務器的讀寫訪問壓力過大的問題,同時也可以給爲數據備份及冗餘提供一種解決方案;
  7. 爲了編碼主redis服務器寫磁盤壓力帶來的開銷,可以配置讓主redis不在將數據持久化到磁盤,而是通過連接讓一個配置的從redis服務器及時的將相關數據持久化到磁盤,
    不過這樣會存在一個問題,就是主redis服務器一旦重啓,因爲主redis服務器數據爲空,這時候通過主從同步可能導致從redis服務器上的數據也被清空;

主從同步時的幾個問題

1. 在上面的全量同步過程中,master會將數據保存在rdb文件中然後發送給slave服務器,但是如果master上的磁盤空間有效怎麼辦呢?那麼此時全部同步對於master來說
將是一份十分有壓力的操作了。此時可以通過無盤複製來達到目的,由master直接開啓一個socket將rdb文件發送給slave服務器。(無盤複製一般應用在磁盤空間有限但是網
絡狀態良好的情況下)
 
2. 主從複製結構,一般slave服務器不能進行寫操作,但是這不是死的,之所以這樣是爲了更容易的保證主和各個從之間數據的一致性,如果slave服務器上數據進行了修改,
那麼要保證所有主從服務器都能一致,可能在結構上和處理邏輯上更爲負責。不過你也可以通過配置文件讓從服務器支持寫操作。(不過所帶來的影響還得自己承擔哦。。。)
 
3. 主從服務器之間會定期進行通話,但是如果master上設置了密碼,那麼如果不給slave設置密碼就會導致slave不能跟master進行任何操作,所以如果你的master服務器
上有密碼,那麼也給slave相應的設置一下密碼吧(通過設置配置文件中的masterauth);
 
4. 關於slave服務器上過期鍵的處理,由master服務器負責鍵的過期刪除處理,然後將相關刪除命令已數據同步的方式同步給slave服務器,slave服務器根據刪除命令刪除
本地的key。

當主服務器不能持久化時複製的安全性

在進行主從複製設置時,強烈建議在主服務器上開啓持久化,當不能這麼做時,比如考慮到延遲的問題,應該將實例配置爲避免自動重啓。
 
爲什麼不持久化的主服務器自動重啓非常危險呢?
爲了更好的理解這個問題,看下面這個失敗的例子,其中主服務器和從服務器中數據庫都被刪除了。
 
設置節點A爲主服務器,關閉持久化,節點B和C從節點A複製數據。
這時出現了一個崩潰,但Redis具有自動重啓系統,重啓了進程,因爲關閉了持久化,節點重啓後只有一個空的數據集。
節點B和C從節點A進行復制,現在節點A是空的,所以節點B和C上的複製數據也會被刪除。
當在高可用系統中使用Redis Sentinel,關閉了主服務器的持久化,並且允許自動重啓,這種情況是很危險的。
比如主服務器可能在很短的時間就完成了重啓,以至於Sentinel都無法檢測到這次失敗,那麼上面說的這種失敗的情況就發生了。
 
如果數據比較重要,並且在使用主從複製時關閉了主服務器持久化功能的場景中,都應該禁止實例自動重啓。

只讀服務器

從Redis 2.6開始,從服務器支持只讀模式,並且是默認模式。這個行爲是由Redis.conf文件中的slave-read-only 參數控制的,
可以在運行中通過CONFIG SET來啓用或者禁用。
 
只讀的從服務器會拒絕所有寫命令,所以對從服務器不會有誤寫操作。但這不表示可以把從服務器實例暴露在危險的網絡環境下,
因爲像DEBUG或者CONFIG這樣的管理命令還是可以運行的。不過你可以通過使用rename-command命令來爲這些命令改名來增加安全性。
 
你可能想知道爲什麼只讀限制還可以被還原,使得從服務器還可以進行寫操作。雖然當主從服務器進行重新同步或者從服務器重啓後,
這些寫操作都會失效,還是有一些使用場景會想從服務器中寫入臨時數據的,但將來這個特性可能會被去掉。

限制有N個以上服務器才允許寫入

從Redis 2.8版本開始,可以配置主服務器連接N個以上從服務器才允許對主服務器進行寫操作。但是,因爲Redis使用的是異步主從複製,
沒辦法確保從服務器確實收到了要寫入的數據,所以還是有一定的數據丟失的可能性。
 
這一特性的工作原理如下:
1. 從服務器每秒鐘ping一次主服務器,確認處理的複製流數量。
2. 主服務器記住每個從服務器最近一次ping的時間。
3. 用戶可以配置最少要有N個服務器有小於M秒的確認延遲。
4. 如果有N個以上從服務器,並且確認延遲小於M秒,主服務器接受寫操作。
 
還可以把這看做是CAP原則(一致性,可用性,分區容錯性)不嚴格的一致性實現,雖然不能百分百確保一致性,但至少保證了丟失的數據不會超過M秒內的數據量。
 
如果條件不滿足,主服務器會拒絕寫操作並返回一個錯誤。
1. min-slaves-to-write(最小從服務器數)
2. min-slaves-max-lag(從服務器最大確認延遲)
3. 主服務器記住每個從服務器最近一次ping的時間。
4. 用戶可以配置最少要有N個服務器有小於M秒的確認延遲。
5. 如果有N個以上從服務器,並且確認延遲小於M秒,主服務器接受寫操作。
 
還可以把這看做是CAP原則(一致性,可用性,分區容錯性)不嚴格的一致性實現,雖然不能百分百確保一致性,但至少保證了丟失的數據不會超過M秒內的數據量。
 
如果條件不滿足,主服務器會拒絕寫操作並返回一個錯誤。
1.min-slaves-to-write(最小從服務器數)
2.min-slaves-max-lag(從服務器最大確認延遲)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章