關於Redis主從複製原理,史上講解最好的一篇文章!

和Mysql主從複製的原因一樣,Redis雖然讀取寫入的速度都特別快,但是也會產生讀壓力特別大的情況。爲了分擔讀壓力,Redis支持主從複製,Redis的主從結構可以採用一主多從或者級聯結構,Redis主從複製可以根據是否是全量分爲全量同步和增量同步。下圖爲級聯結構。

全量同步

Redis全量複製一般發生在Slave初始化階段,這時Slave需要將Master上的所有數據都複製一份。具體步驟如下:

-  從服務器連接主服務器,發送SYNC命令; 
-  主服務器接收到SYNC命名後,開始執行BGSAVE命令生成RDB文件並使用緩衝區記錄此後執行的所有寫命令; 
-  主服務器BGSAVE執行完後,向所有從服務器發送快照文件,並在發送期間繼續記錄被執行的寫命令; 
-  從服務器收到快照文件後丟棄所有舊數據,載入收到的快照; 
-  主服務器快照發送完畢後開始向從服務器發送緩衝區中的寫命令; 
-  從服務器完成對快照的載入,開始接收命令請求,並執行來自主服務器緩衝區的寫命令;

完成上面幾個步驟後就完成了從服務器數據初始化的所有操作,從服務器此時可以接收來自用戶的讀請求。

增量同步

Redis增量複製是指Slave初始化後開始正常工作時主服務器發生的寫操作同步到從服務器的過程。

增量複製的過程主要是主服務器每執行一個寫命令就會向從服務器發送相同的寫命令,從服務器接收並執行收到的寫命令。

Redis主從同步策略

主從剛剛連接的時候,進行全量同步;全同步結束後,進行增量同步。當然,如果有需要,slave 在任何時候都可以發起全量同步。redis 策略是,無論如何,首先會嘗試進行增量同步,如不成功,要求從機進行全量同步。

注意點

如果多個Slave斷線了,需要重啓的時候,因爲只要Slave啓動,就會發送sync請求和主機全量同步,當多個同時出現的時候,可能會導致Master IO劇增宕機。

Redis主從複製的配置十分簡單,它可以使從服務器是主服務器的完全拷貝。需要清除Redis主從複製的幾點重要內容:

1)Redis使用異步複製。但從Redis 2.8開始,從服務器會週期性的應答從複製流中處理的數據量。
2)一個主服務器可以有多個從服務器。
3)從服務器也可以接受其他從服務器的連接。除了多個從服務器連接到一個主服務器之外,多個從服務器也可以連接到一個從服務器上,形成一個
圖狀結構。
4)Redis主從複製不阻塞主服務器端。也就是說當若干個從服務器在進行初始同步時,主服務器仍然可以處理請求。
5)主從複製也不阻塞從服務器端。當從服務器進行初始同步時,它使用舊版本的數據來應對查詢請求,假設你在redis.conf配置文件是這麼配置的。
否則的話,你可以配置當複製流關閉時讓從服務器給客戶端返回一個錯誤。但是,當初始同步完成後,需要刪除舊的數據集和加載新的數據集,在
這個短暫的時間內,從服務器會阻塞連接進來的請求。
6)主從複製可以用來增強擴展性,使用多個從服務器來處理只讀的請求(比如,繁重的排序操作可以放到從服務器去做),也可以簡單的用來做數據冗餘。
7)使用主從複製可以爲主服務器免除把數據寫入磁盤的消耗:在主服務器的redis.conf文件中配置“避免保存”(註釋掉所有“保存“命令),然後連接一個配
置爲“進行保存”的從服務器即可。但是這個配置要確保主服務器不會自動重啓(要獲得更多信息請閱讀下一段)

主從複製的一些特點:

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服務器上的數據也被清空;

Redis大概主從同步是怎麼實現的?

全量同步:

master服務器會開啓一個後臺進程用於將redis中的數據生成一個rdb文件,與此同時,服務器會緩存所有接收到的來自客戶端的寫命令(包含增、刪、改),當後臺保存進程
處理完畢後,會將該rdb文件傳遞給slave服務器,而slave服務器會將rdb文件保存在磁盤並通過讀取該文件將數據加載到內存,在此之後master服務器會將在此期間緩存的
命令通過redis傳輸協議發送給slave服務器,然後slave服務器將這些命令依次作用於自己本地的數據集上最終達到數據的一致性。

部分同步:

從redis 2.8版本以前,並不支持部分同步,當主從服務器之間的連接斷掉之後,master服務器和slave服務器之間都是進行全量數據同步,但是從redis 2.8開
始,即使主從連接中途斷掉,也不需要進行全量同步,因爲從這個版本開始融入了部分同步的概念。部分同步的實現依賴於在master服務器內存中給每個slave服務器維護了
一份同步日誌和同步標識,每個slave服務器在跟master服務器進行同步時都會攜帶自己的同步標識和上次同步的最後位置。當主從連接斷掉之後,slave服務器隔斷時間
(默認1s)主動嘗試和master服務器進行連接,如果從服務器攜帶的偏移量標識還在master服務器上的同步備份日誌中,那麼就從slave發送的偏移量開始繼續上次的同步
操作,如果slave發送的偏移量已經不再master的同步備份日誌中(可能由於主從之間斷掉的時間比較長或者在斷掉的短暫時間內master服務器接收到大量的寫操作),則
必須進行一次全量更新。在部分同步過程中,master會將本地記錄的同步備份日誌中記錄的指令依次發送給slave服務器從而達到數據一致。

主從同步中需要注意幾個問題

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主從複製是如何工作的

如果設置了一個從服務器,在連接時它發送了一個SYNC命令,不管它是第一次連接還是再次連接都沒有關係。

然後主服務器開始後臺存儲,並且開始緩存新連接進來的修改數據的命令。當後臺存儲完成後,主服務器把數據文件發送到從服務器,

從服務器將其保存在磁盤上,然後加載到內存中。然後主服務器把剛纔緩存的命令發送到從服務器。這是作爲命令流來完成的,並且

和Redis協議本身格式相同。

你可以通過telnet自己嘗試一下。在Redis服務器工作時連接到Redis端口,發送SYNC命令,會看到一個批量的傳輸,並且主服務器接收

的每一個命令都會通過telnet會話重新發送一遍。

當主從服務器之間的連接由於某些原因斷開時,從服務器可以自動進行重連接。當有多個從服務器同時請求同步時,主服務器只進行一個後臺存儲。

當連接斷開又重新連上之後,一般都會進行一個完整的重新同步,但是從Redis2.8開始,只重新同步一部分也可以。

部分重新同步

從Redis 2.8開始,如果遭遇連接斷開,重新連接之後可以從中斷處繼續進行復制,而不必重新同步。

它的工作原理是這樣:

主服務器端爲複製流維護一個內存緩衝區(in-memory backlog)。主從服務器都維護一個複製偏移量(replication offset)和master run id ,

當連接斷開時,從服務器會重新連接上主服務器,然後請求繼續複製,假如主從服務器的兩個master run id相同,並且指定的偏移量在內存緩衝

區中還有效,複製就會從上次中斷的點開始繼續。如果其中一個條件不滿足,就會進行完全重新同步(在2.8版本之前就是直接進行完全重新同步)。

因爲主運行id不保存在磁盤中,如果從服務器重啓了的話就只能進行完全同步了。

部分重新同步這個新特性內部使用PSYNC命令,舊的實現中使用SYNC命令。Redis2.8版本可以檢測出它所連接的服務器是否支持PSYNC命令,不支持的

話使用SYNC命令。

無磁盤複製

通常來講,一個完全重新同步需要在磁盤上創建一個RDB文件,然後加載這個文件以便爲從服務器發送數據。

如果使用比較低速的磁盤,這種操作會給主服務器帶來較大的壓力。Redis從2.8.18版本開始嘗試支持無磁盤的複製。

使用這種設置時,子進程直接將RDB通過網絡發送給從服務器,不使用磁盤作爲中間存儲。

配置

主從複製的配置十分簡單:把下面這行加入到從服務器的配置文件中即可。

slaveof 192.168.1.1 6379

當然你需要把其中的192.168.1.1 6379替換爲你自己的主服務器IP(或者主機名hostname)和端口。另外你可以調用SLAVEOF命令,

主服務器就會開始與從服務器同步。

關於部分重新同步,還有一些針對複製內存緩衝區的優化參數。查看Redis介質中的Redis.conf示例獲得更多信息。

使用repl-diskless-sync配置參數來啓動無磁盤複製。使用repl-diskless-sync-delay 參數來配置傳輸開始的延遲時間,以便等待

更多的從服務器連接上來。查看Redis介質中的Redis.conf示例獲得更多信息。

只讀從服務器

從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(從服務器最大確認延遲)

通過redis實現服務器崩潰等數據恢復

由於redis存儲在內存中且提供一般編程語言常用的數據結構存儲類型,所以經常被用於做服務器崩潰宕機的數據恢復處理。服務器可以在某些指定過程中將需要保存的數據以json對象等方式存儲到redis中,也就是我們常說的快照,當服務器運行時讀取redis來判斷是否有待需要恢復數據繼續處理的業務。當一次業務處理結束後再刪除redis的數據即可。redis提供兩種將內存數據導出到硬盤實現數據備份的方法

1)RDB方式(默認)

RDB方式的持久化是通過快照(snapshotting)完成的,當符合一定條件時Redis會自動將內存中的所有數據進行快照並存儲在硬盤上。進行快照的條件可以由用戶在配置文件中自定義,由兩個參數構成:時間和改動的鍵的個數。當在指定的時間內被更改的鍵的個數大於指定的數值時就會進行快照。RDB是redis默認採用的持久化方式,在配置文件中已經預置了3個條件:

save 900 1      #900秒內有至少1個鍵被更改則進行快照
save 300 10     #300秒內有至少10個鍵被更改則進行快照
save 60 10000   #60秒內有至少10000個鍵被更改則進行快照

可以存在多個條件,條件之間是"或"的關係,只要滿足其中一個條件,就會進行快照。 如果想要禁用自動快照,只需要將所有的save參數刪除即可。

Redis默認會將快照文件存儲在當前目錄(可CONFIG GET dir來查看)的dump.rdb文件中,可以通過配置dir和dbfilename兩個參數分別指定快照文件的存儲路徑和文件名。

Redis實現快照的過程

-  Redis使用fork函數複製一份當前進程(父進程)的副本(子進程);
-  父進程繼續接收並處理客戶端發來的命令,而子進程開始將內存中的數據寫入硬盤中的臨時文件;
-  當子進程寫入完所有數據後會用該臨時文件替換舊的RDB文件,至此一次快照操作完成。
-  在執行fork的時候操作系統(類Unix操作系統)會使用寫時複製(copy-on-write)策略,即fork函數發生的一刻父子進程共享同一內存數據,當父進程要更改其中某片數據時(如執行一個寫命令 ),操作系統會將該片數據複製一份以保證子進程的數據不受影響,所以新的RDB文件存儲的是執行fork一刻的內存數據。

Redis在進行快照的過程中不會修改RDB文件,只有快照結束後纔會將舊的文件替換成新的,也就是說任何時候RDB文件都是完整的。這使得我們可以通過定時備份RDB文件來實 現Redis數據庫備份。RDB文件是經過壓縮(可以配置rdbcompression參數以禁用壓縮節省CPU佔用)的二進制格式,所以佔用的空間會小於內存中的數據大小,更加利於傳輸。

除了自動快照,還可以手動發送SAVE或BGSAVE命令讓Redis執行快照,兩個命令的區別在於,前者是由主進程進行快照操作,會阻塞住其他請求,後者會通過fork子進程進行快照操作。 Redis啓動後會讀取RDB快照文件,將數據從硬盤載入到內存。根據數據量大小與結構和服務器性能不同,這個時間也不同。通常將一個記錄一千萬個字符串類型鍵、大小爲1GB的快照文件載入到內 存中需要花費20~30秒鐘。 通過RDB方式實現持久化,一旦Redis異常退出,就會丟失最後一次快照以後更改的所有數據。這就需要開發者根據具體的應用場合,通過組合設置自動快照條件的方式來將可能發生的數據損失控制在能夠接受的範圍。如果數據很重要以至於無法承受任何損失,則可以考慮使用AOF方式進行持久化。

2)AOF方式

默認情況下Redis沒有開啓AOF(append only file)方式的持久化,可以在redis.conf中通過appendonly參數開啓:

appendonly yes

在啓動時Redis會逐個執行AOF文件中的命令來將硬盤中的數據載入到內存中,載入的速度相較RDB會慢一些

開啓AOF持久化後每執行一條會更改Redis中的數據的命令,Redis就會將該命令寫入硬盤中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通過dir參數設置的,默認的文件名是appendonly.aof,可以通過appendfilename參數修改:

appendfilename appendonly.aof

配置redis自動重寫AOF文件的條件

auto-aof-rewrite-percentage 100  # 當目前的AOF文件大小超過上一次重寫時的AOF文件大小的百分之多少時會再次進行重寫,如果之前沒有重寫過,則以啓動時的AOF文件大小爲依據

auto-aof-rewrite-min-size 64mb   # 允許重寫的最小AOF文件大小

配置寫入AOF文件後,要求系統刷新硬盤緩存的機制

# appendfsync always   # 每次執行寫入都會執行同步,最安全也最慢
appendfsync everysec   # 每秒執行一次同步操作

# appendfsync no       # 不主動進行同步操作,而是完全交由操作系統來做(即每30秒一次),最快也最不安全

Redis允許同時開啓AOF和RDB,既保證了數據安全又使得進行備份等操作十分容易。此時重新啓動Redis後Redis會使用AOF文件來恢復數據,因爲AOF方式的持久化可能丟失的數據更少

當你發現自己的才華撐不起野心時,就請安靜下來學習吧

作者:老虎死了還有狼
鏈接:https://www.cnblogs.com/daofaziran/p/10978628.html
鏈接:博客園

補充

1、書籍項目:

codeGoogler/ProgramBooks

2:視頻教程:

程序員必讀Java書籍SpringBoot、Spring、Mybatis、Redis、RabbitMQ、SpringCloud、高併發

覺得不錯的小夥伴記得點贊和蒐藏哦,後續會持續更新精選技術文章!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章