一、主從復制
1、redis為什么還要主從復制?
和Mysql主從復制的原因一樣,Redis雖然讀取寫入的速度都特別快,但是也會產生讀壓力特別大的情況。為了分擔讀壓力,Redis支持主從復制,Redis的主從結構可以采用一主多從或者級聯結構,Redis主從復制可以根據是否是全量分為全量同步和增量同步。
2、什么是主從復制?
下圖為級聯結構
主從復制,是指將一台Redis服務器的數據,復制到其他的Redis服務器。前者稱為主節點(master),后者稱為從節點(slave),數據的復制是單向的,只能由主節點到從節點。
默認情況下,每台Redis服務器都是主節點;且一個主節點可以有多個從節點(或沒有從節點),但一個從節點只能有一個主節點。
3、主從復制的作用
1. 數據冗余:主從復制實現了數據的熱備份,是持久化之外的一種數據冗余方式。
2. 故障恢復:當主節點出現問題時,可以由從節點提供服務,實現快速的故障恢復;實際上是一種服務的冗余。
3. 負載均衡:在主從復制的基礎上,配合讀寫分離,可以由主節點提供寫服務,由從節點提供讀服務(即寫Redis數據時應用連接主節點,讀Redis數據時應用連接從節點),分擔服務器負載;尤其是在寫少讀多的場景下,通過多個從節點分擔讀負載,可以大大提高Redis服務器的並發量。
4. 讀寫分離:可以用於實現讀寫分離,主庫寫、從庫讀,讀寫分離不僅可以提高服務器的負載能力,同時可根據需求的變化,改變從庫的數量;
5. 高可用基石:除了上述作用以外,主從復制還是哨兵和集群能夠實施的基礎,因此說主從復制是Redis高可用的基礎。
4、主從復制的方式
4.1 全量同步
Redis全量復制一般發生在Slave初始化階段,這時Slave需要將Master上的所有數據都復制一份。具體步驟如下:
- 從服務器連接主服務器,發送SYNC命令;
- 主服務器接收到SYNC命名后,開始執行BGSAVE命令生成RDB文件並使用緩沖區記錄此后執行的所有寫命令;
- 主服務器BGSAVE執行完后,向所有從服務器發送快照文件,並在發送期間繼續記錄被執行的寫命令;
- 從服務器收到快照文件后丟棄所有舊數據,載入收到的快照;
- 主服務器快照發送完畢后開始向從服務器發送緩沖區中的寫命令;
- 從服務器完成對快照的載入,開始接收命令請求,並執行來自主服務器緩沖區的寫命令;
完成上面幾個步驟后就完成了從服務器數據初始化的所有操作,從服務器此時可以接收來自用戶的讀請求。
4.2 增量同步
Redis增量復制是指Slave初始化后開始正常工作時主服務器發生的寫操作同步到從服務器的過程。
增量復制的過程主要是主服務器每執行一個寫命令就會向從服務器發送相同的寫命令,從服務器接收並執行收到的寫命令。
Redis主從同步策略
主從剛剛連接的時候,進行全量同步;全同步結束后,進行增量同步。當然,如果有需要,slave 在任何時候都可以發起全量同步。redis 策略是,無論如何,首先會嘗試進行增量同步,如不成功,要求從機進行全量同步。
5、主從復制的一些特點:
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服務器從而達到數據一致。
主從同步中需要注意幾個問題
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方式的持久化可能丟失的數據更少
主從復制啟用
從節點開啟主從復制,有3種方式:
1. 配置文件: 在從服務器的配置文件中加入:slaveof <masterip> <masterport>
2. 啟動命令: redis-server啟動命令后加入 --slaveof <masterip> <masterport>
3. 客戶端命令: Redis服務器啟動后,直接通過客戶端執行命令:slaveof <masterip>
<masterport>,則該Redis實例成為從節點。
通過 info replication 命令可以看到復制的一些信息
主從復制原理
主從復制過程大體可以分為3個階段:連接建立階段(即准備階段)、數據同步階段、命令傳播階段。
在從節點執行 slaveof 命令后,復制過程便開始運作,下面圖示大概可以看到,
從圖中可以看出復制過程大致分為6個過程
主從配置之后的日志記錄也可以看出這個流程
1)保存主節點(master)信息。
執行 slaveof 后 Redis 會打印如下日志:
2)從節點(slave)內部通過每秒運行的定時任務維護復制相關邏輯,當定時任務發現存在新的主節點后,會嘗試與該節點建立網絡連接
從節點與主節點建立網絡連接
從節點會建立一個 socket 套接字,從節點建立了一個端口為51234的套接字,專門用於接受主節點發送的復制命令。從節點連接成功后打印如下日志:
如果從節點無法建立連接,定時任務會無限重試直到連接成功或者執行 slaveof no one 取消復制
關於連接失敗,可以在從節點執行 info replication 查看 master_link_down_since_seconds 指標,它會記錄與主節點連接失敗的系統時間。從節點連接主節點失敗時也會每秒打印如下日志,方便發現問題:
# Error condition on socket for SYNC: {socket_error_reason}
3)發送 ping 命令。
連接建立成功后從節點發送 ping 請求進行首次通信,ping 請求主要目的如下:
·檢測主從之間網絡套接字是否可用。
·檢測主節點當前是否可接受處理命令。
如果發送 ping 命令后,從節點沒有收到主節點的 pong 回復或者超時,比如網絡超時或者主節點正在阻塞無法響應命令,從節點會斷開復制連接,下次定時任務會發起重連。
從節點發送的 ping 命令成功返回,Redis 打印如下日志,並繼續后續復制流程:
4)權限驗證。如果主節點設置了 requirepass 參數,則需要密碼驗證,從節點必須配置 masterauth 參數保證與主節點相同的密碼才能通過驗證;如果驗證失敗復制將終止,從節點重新發起復制流程。
5)同步數據集。主從復制連接正常通信后,對於首次建立復制的場景,主節點會把持有的數據全部發送給從節點,這部分操作是耗時最長的步驟。
6)命令持續復制。當主節點把當前的數據同步給從節點后,便完成了復制的建立流程。接下來主節點會持續地把寫命令發送給從節點,保證主從數據一致性。
轉:https://zhuanlan.zhihu.com/p/112213386
https://www.cnblogs.com/kevingrace/p/5685332.html
https://www.cnblogs.com/shujiying/p/14204098.html