以前總結過redis的持久化機制:深度剖析Redis持久化機制,持久化
機制主要解決redis數據單機備份問題;redis的高可用須要考慮數據的多機備份,多機備份經過主從複製
來實現,這是redis高可用的基石。本文將詳細介紹redis主從複製的實現原理,在使用過程當中應該注意的問題和相關配置。redis
CAP理論是分佈式領域的牛頓定律,全部的分佈式存儲中間件都要使用它做爲理論基石。以下圖所示:bash
這個原理很簡單,首先明確幾個概念:服務器
分佈式系統的節點每每分佈在不一樣的機器上,它們之間由網絡進行隔離,當網絡斷開時就會產生網絡分區
。網絡分區不可避免,可是當網絡分區發生時,對分佈式系統中一個節點的修改操做沒法同步給其它節點,數據一致性
也就沒法知足;想要知足一致性
,除非犧牲可用性
,也就是暫停分佈式節點服務,等到網絡恢復,數據一致後,再對外提供服務。分佈式系統中網絡分區
不可避免,一致性
和可用性
水火不容。這就是cap理論:網絡分區發生時,一致性和可用性兩難全。網絡
互聯網圈常常談「三高」架構:高併發、高性能、高可用。對於redis來講,高併發、高性能能夠保證,高可用須要架構的設計,單機redis因爲存在系統崩潰、硬盤故障的風險,還有內存的限制,因此企業通常都會搭建主從,對系統有更高要求會搭建集羣。架構
爲了保持數據一致性,主節點(master)只寫,從節點(slave)只讀,數據由主節點複製給從節點,這個複製的過程就是主從複製。併發
redis的主從複製是異步的,分佈式的redis系統並不知足一致性要求,可是在網絡斷開的狀況下,主節點依然能夠對外提供服務,知足可用性。redis保證最終一致性,從節點會努力追趕主節點,最終從節點的狀態會和從節點保持一致。網絡斷開的狀況下,主從節點數據會出現大量的不一致,但一旦網絡恢復,從節點會繼續追趕主節點,最終達到和主節點狀態一致。負載均衡
爲了減輕redis主節點的同步負擔,redis 的後續版本還增長了從從同步,與此同時,數據一致性會變差。異步
主從複製在服務中起到了什麼效果呢?socket
有三種配置實現redis的主從:分佈式
slaveof <masterip> <masterport>
複製代碼
redis-server -slaveof <masterip> <masterport>
複製代碼
slaveof <masterip> <masterport>
複製代碼
比較主流的用法是經過配置文件的方式實現主從。還有其它的一些命令:
slaveof no one
複製代碼
#master有兩種方式設置
//-- 1.master配置文件中設置
requirepass <password>
//-- 2.master客戶端發送命令設置密碼
config set requirepass <password>
config get requirepass
#slave有三種方式實現認證
//-- 1.客戶端發送命令設置密碼
auth <password>
//-- 2.slave配置文件設置密碼
masterauth <password>
//-- 3.啓動客戶端設置密碼
redis-cli -a <password>
複製代碼
redis主從複製實現過程有三個階段:
創建鏈接、數據同步、命令傳播。創建鏈接階段主從節點創建通訊的橋樑,彼此之間同步一些基礎信息;數據同步階段實現從節點全量同步主節點的數據;從節點同步完主節點數據以後,就進入了命令傳播階段,主節點接收寫請求,數據不斷髮生變化,經過命令傳播階段主節點將數據源源不斷的同步給從節點。下邊咱們詳細介紹主從複製這三個階段的工做細節和注意事項。
創建slave到master的鏈接,使master能識別slave, 並保存slave的端口號;與此同時,slave也保存master的地址和端口號信息。
slaveof ip port
命令給master,master響應slave經過以上過程主從之間的鏈接就創建了。
數據同步階段實現的功能是從節點從主節點同步全量的數據。這個過程又分爲幾個小階段,最主要的就是數據的全量複製
和部分複製
, 對應的流程就是主節點發送rdb文件同步數據和發送緩衝區寫命令(aof)同步數據給從節點。下圖是實現細節:
首先slave節點先發起命令psync ? -1
,向master節點要全量數據。
master節點接收到指令之後,執行bgsave,將當前內存數據快照保存爲rdb文件,這個過程爲了避免影響主節點繼續對外提供服務,採用了Copy On Write
技術。與此同時,master節點也會將bgsave保存快照期間接收到的寫更新命令添加到複製擠壓緩衝區
當中。master節點rdb文件生成完畢之後,會經過第一階段創建的socket鏈接將它發送給slave節點,還會發送+FULLRESYNC runid offset
給slave節點,告訴slave節點本身的runid
和offset
。
什麼是
runid
?
redis-server
在每次啓動的時候都會生成一個runid,由於redis-server是一個守護進程,因此在運行期間,runid不會發生變化,能夠經過info server
指令查看runid,它是一個40位字符長度的字符串。上文提到的psync
有兩個參數,和+FULLRESYNC
同樣:psync <runid> <offset>
;runid的意義是什麼呢?當master節點發生故障發生了變動後,在接到slave的指令之後,對比參數中的runid若是和本身的runid不一致,就會再次進行全量複製,由於換主了。
什麼是
複製擠壓緩衝區
和offset
?複製擠壓緩衝區是一個先進先出(FIFO)的環形隊列,用於存儲服務端執行過的命令,每次傳播命令,master節點都會將傳播的命令記錄下來,保存在這裏。
複製擠壓緩衝區由兩部分組成:偏移量和字節值。字節值是redis指令字節的存儲(redis指令以一種Redis序列化文本協議的格式存儲),偏移量offset就是當前字節值在環形隊列中的偏移量。 ![]()
![]()
slave節點接收完master節點同步的rdb文件以後,將rdb的內容加載到本身的內存,而後將master節點的runid
和offset
記錄下來。
有了master節點的runid
和offset
,在加載完rdb文件以後,就開始向master節點發送新的命令psync runid offset
,向master節點要新數據。新數據是master節點在bgsave生成rdb文件時和向slave同步數據的這段時間產生的,因此這段時間的工做也稱爲部分複製
。
master節點收到slave節點發送的請求數據命令以後,會檢查runid是否一致(是否換主),offset是否一致(由於複製擠壓緩衝區是定長的,全部有可能會溢出),這兩個條件只要有一個不知足,master就會向slave再次全量的同步數據(讀者可能會發現,若是master節點寫併發很高,複製擠壓緩衝區又設置的比較小的話,可能會每次向slave同步完數據之後,每次複製擠壓緩衝區都會溢出,形成主從之間循環的全量複製。這確實是應該規避的問題!咱們後邊會針對主從複製應該考慮的問題作一個總結)。在runid和offset都知足的狀況下,master節點就會向slave節點發送指令+CONTINUE offset
,接着從offset位置開始同步數據,數據都在主節點的複製擠壓緩衝區中了,因此直接複製發送就能夠了。
slave節點接收到master節點發送的+CONTINUE offset
指令以後,更新本身保存的offset值,而後將從master節點同步過來的數據,使用bgrewriteaof,重放aof數據。
到這裏,主從複製的第二階段:數據同步階段工做就完成了。
命令傳播階段相似於數據同步階段的部分複製
,當master節點數據被修改之後,就和slave節點的數據不一致了,這個時候master節點就會根據slave上報的offset開始傳播數據(一主多從的架構中,master節點要記錄每個slave的offset)。slave接收到數據之後,執行bgrewriteaof重放數據。在這個工做過程當中,若是由於網路問題致使offset溢出或者換主的狀況,主從之間仍是會進行數據的全量同步的。
進入命令傳播階段之後,master節點與slave節點須要進行信息傳遞,使用心跳機制進行維護,實現雙方保持在線。
master節點心跳使用指令PING
,由配置repl-ping-slave-period
決定,默認10秒,做用是判斷slave是否在線,能夠經過info replication
獲取slave最後一次鏈接到如今的時間間隔,lag的值維護在0和1視爲正常。
slave節點的心跳任務使用指令REPLCONF ACK {offset}
,週期是1秒,slave的心跳任務有兩個做用:
在心跳階段應該注意:當slave節點多數掉線,或者延遲太高時,master節點爲了保證數據的穩定性,將拒絕全部信息的同步。有以下配置:
min-slaves-to-write 2
min-slaves-max-lag 8
複製代碼
上述配置含義是:當slave數量小於2個,或者全部的slave的延遲都大於等於8秒時,強制關閉master寫功能,中止數據同步。
上邊介紹的主從複製是創建在主從節點間的網絡和服務都正常的狀況下,業務場景中要考慮更多的實際狀況。
伴隨着系統的運行,master節點的內存數據量變得很大的狀況下,一旦master節點重啓,runid將發生變化,會致使slave的全量複製操做。
這裏有一個優化方案:在master節點內部建立master_replid變量,使用runid相同的策略生成,長度41位,發送給全部的slave節點。在master節點關閉時,執行命令shutdown save,進行RDB的數據持久化,將runid與offset保存在RDB文件中。在RDB文件中有了repl-id和repl-offset信息之後,經過指令redis-check-rdb
命令能夠查看這些信息。在master節點重啓後,將RDB文件加載到內存中之後,也會將repl-id
和repl-offset
加載到內存中。經過info 指令能夠查看:
master_repl_id = repl
master_repl_offset = repl-offset
複製代碼
做用是:master節點重啓以後會保存原來的runid,重啓後恢復該值,會讓全部的slave節點認爲仍是以前的master節點。
當複製積壓緩衝區過小的時候,當master節點寫併發很大,master節點和slave節點網絡有抖動的時候,就會致使數據同步不及時,形成offset溢出,進而致使全量複製
。這個時候,咱們能夠考慮修改複製積壓緩衝區的大小,由配置repl-backlog-size
控制。設置多大比較合適呢,這要根據master的併發量和網絡狀況作具體的評估。
前邊內容咱們提到slave節點每秒都會發送REPLCONF ACK指令到master節點,master節點調用複製函數relicationCron()同步數據給slave節點時,若是slave節點執行了keys *、hgetall等阻塞命令的時候,就會在很長一段時候得不到響應。這就會致使master的各類資源(輸出緩衝區、帶寬、鏈接)等被佔用。master節點的CPU就會變高,slave頻繁的斷開鏈接。
解決方案是master節點經過配置:repl-timeout
設置合理的超時時間(默認60s),超過改值,master節點將釋放slave節點。
master節點默認10s向slave節點發送一次ping指令,由於master節點不只要處理大量的寫任務,還可能維護着多個master,因此ping設置的不太及時。可是當ping指令在網絡中存在丟包時,master節點若是設置的超時時間過短,就會致使master節點與slave節點斷開鏈接。
解決方案有:提升master節點ping的頻度,超時時間repl-time設置爲ping指令時間的5~10倍。
當主從同步中網絡數據發送有延遲的時候,就會形成多個slave獲取到的數據不一樣步,解決方案是優化master節點和slave節點的網絡環境,一般是放置在一個機房部署。另外要監控master和slave節點的延遲,若是延遲過大,能夠暫時屏蔽對slave節點的訪問。經過下面指令設置:
slave-serve-stale-data yes | no
複製代碼
開啓後,slave節點僅僅能響應info、slaveof等少數命令,除非對數據一致性要求很高,不然不要輕易這樣使用。
本文主要總結了redis實現主從複製的實現細節和注意事項。redis的主從複製是實現高可用的重要基石,後邊的文章將總結哨兵和集羣的搭建。