【轉】Redis主從同步與集群管理


轉自http://blog.csdn.net/u012152619/article/details/52854465

1.主從同步原理

像MySQL一樣,Redis是支持主從同步的,而且也支持一主多從以及多級從結構。
主從結構,一是為了純粹的冗余備份,二是為了提升讀性能,比如很消耗性能的SORT就可以由從服務器來承擔。
Redis的主從同步是異步進行的,這意味着主從同步不會影響主邏輯,也不會降低Redis的處理性能。
主從架構中,可以考慮關閉主服務器的數據持久化功能,只讓從服務器進行持久化,這樣可以提高主服務器的處理性能。
在主從架構中,從服務器通常被設置為只讀模式,這樣可以避免從服務器的數據被誤修改。但是從服務器仍然可以接受CONFIG等指令,所以還是不應該將從服務器直接暴露到不安全的網絡環境中。如果必須如此,那可以考慮給重要指令進行重命名,來避免命令被外人誤執行。

 

從服務器會向主服務器發出SYNC指令,當主服務器接到此命令后,就會調用BGSAVE指令來創建一個子進程專門進行數據持久化工作,也就是將主服務器的數據寫入RDB文件中。在數據持久化期間,主服務器將執行的寫指令都緩存在內存中。
在BGSAVE指令執行完成后,主服務器會將持久化好的RDB文件發送給從服務器,從服務器接到此文件后會將其存儲到磁盤上,然后再將其讀取到內存中。這個動作完成后,主服務器會將這段時間緩存的寫指令再以Redis協議的格式發送給從服務器。

 

另外,要說的一點是,即使有多個從服務器同時發來SYNC指令,主服務器也只會執行一次BGSAVE,然后把持久化好的RDB文件發給多個下游。在Redis2.8版本之前,如果從服務器與主服務器因某些原因斷開連接的話,都會進行一次主從之間的全量的數據同步;而在2.8版本之后,Redis支持了效率更高的增量同步策略,這大大降低了連接斷開的恢復成本。

主服務器會在內存中維護一個緩沖區,緩沖區中存儲着將要發給從服務器的內容。從服務器在與主服務器出現網絡瞬斷之后,從服務器會嘗試再次與主服務器連接,一旦連接成功,從服務器就會把“希望同步的主服務器ID”和“希望請求的數據的偏移位置(replication offset)”發送出去。主服務器接收到這樣的同步請求后,首先會驗證主服務器ID是否和自己的ID匹配,其次會檢查“請求的偏移位置”是否存在於自己的緩沖區中,如果兩者都滿足的話,主服務器就會向從服務器發送增量內容。
增量同步功能,需要服務器端支持全新的PSYNC指令。這個指令,只有在Redis-2.8之后才具有。

 

Redis復制工作原理的總結如下:
1. 如果設置了一個Slave,無論是第一次連接還是重連到Master,它都會發出一個SYNC命令;
2. 當Master收到SYNC命令之后,會做兩件事:
a) Master執行BGSAVE,即在后台保存數據到磁盤(rdb快照文件);
b) Master同時將新收到的寫入和修改數據集的命令存入緩沖區(非查詢類);
3. 當Master在后台把數據保存到快照文件完成之后,Master會把這個快照文件傳送給Slave,而Slave則把內存清空后,加載該文件到內存中;
4. 而Master也會把此前收集到緩沖區中的命令,通過Reids命令協議形式轉發給Slave,Slave執行這些命令,實現和Master的同步;
5. Master/Slave此后會不斷通過異步方式進行命令的同步,達到最終數據的同步一致;
6. 需要注意的是Master和Slave之間一旦發生重連都會引發全量同步操作。但在2.8之后版本,也可能是部分同步操作。

 

部分復制
2.8開始,當Master和Slave之間的連接斷開之后,他們之間可以采用持續復制處理方式代替采用全量同步。
Master端為復制流維護一個內存緩沖區(in-memory backlog),記錄最近發送的復制流命令;同時,Master和Slave之間都維護一個復制偏移量(replication offset)和當前Master服務器ID(Masterrun id)。當網絡斷開,Slave嘗試重連時:
a. 如果MasterID相同(即仍是斷網前的Master服務器),並且從斷開時到當前時刻的歷史命令依然在Master的內存緩沖區中存在,則Master會將缺失的這段時間的所有命令發送給Slave執行,然后復制工作就可以繼續執行了;
b. 否則,依然需要全量復制操作;

Redis 2.8 的這個部分重同步特性會用到一個新增的PSYNC 內部命令, 而 Redis 2.8 以前的舊版本只有 SYNC 命令, 不過, 只要從服務器是 Redis 2.8 或以上的版本,它就會根據主服務器的版本來決定到底是使用 PSYNC 還是 SYNC :如果主服務器是 Redis 2.8 或以上版本,那么從服務器使用 PSYNC 命令來進行同步;如果主服務器是 Redis 2.8 之前的版本,那么從服務器使用 SYNC 命令來進行同步。

注意:SYNC命令是一個非常耗費資源的操作

SYNC命令是非常消耗資源的,因為每次執行SYNC命令,主從服務器需要執行一下操作:

  1. 主服務器需要執行BGSAVE命令來生成RDB文件,這個生成操作會耗費主服務器大量的CPU、內存和磁盤I/O資源;
  2. 主服務器需要將自己生成的RDB文件發送給從服務器,這個發送操作會耗費主從服務器大量的網絡資源(帶寬和流量),並對主服務器響應命令請求的時間產生影響;
  3. 接收到RDB文件的從服務器需要載入主服務器發來的RDB文件,並且在載入期間,從服務器會因為阻塞而沒辦法處理命令請求

SYNC是一個如此消耗資源的命令,所以Redis最好在真需要的時候才需要執行SYNC命令。

2.主從同步配置

可以在一台機器上使用不同的端口來模擬多台機器的主從同步。
下面以默認端口6379為Master,以6380、6381端口為兩個Slave。
找到Redis安裝路徑下的redis.conf,復制兩份,分別命名為“redis-6380.conf”與“redis-6381.conf”。
redis.conf相關配置:

pidfile /var/run/redis.pid
port 6379
bind127.0.0.1
logfile redis-3.0.6/log/redis.log
# Mater不進行持久化,關閉RDB與AOF
save “”
appendonly no 

redis-6380.conf相關配置:
pidfile /var/run/redis-6380.pid
port 6380
bind 127.0.0.1
logfile redis-3.0.6/log/redis-6380.log

#Slave同時開啟RDB與AOF dbfilename dump
-6380.rdb dir /Users/winner/program/redis-3.0.6/db/ appendonly yes appendfilename "appendonly-6380.aof" #指定Master的IP與端口 slaveof 127.0.0.1 6379
#Slave設為只讀模式 slave
-read-only yes redis-6381.conf與redis-6380.conf的配置類似。

使用命令“redis-server–h 127.0.0.1 –p 6379”啟動Master實例,再使用“redis-server –h 127.0.0.1 –p 6380”與“redis-server–h 127.0.0.1 –p 6381”啟動兩個Slave實例。
通過set和get實驗同步是否進行。

 

3 Sentinel實現機制與用法

Redis-Sentinel是Redis官方推薦的高可用性(HA,High Available)解決方案,當用Redis做Master-slave的高可用方案時,假如master宕機了,Redis本身(包括它的很多客戶端)都沒有實現自動進行主備切換,而Redis-sentinel本身也是一個獨立運行的進程,它能監控多個master-slave集群,發現master宕機后能進行選舉切換。
它的主要功能有以下幾點
(1)不時地監控redis是否按照預期良好地運行;
(2)如果發現某個redis節點運行出現狀況,能夠通知另外一個進程(例如它的客戶端);
(3)能夠進行自動切換。當一個master節點不可用時,能夠選舉出master的多個slave(如果有超過一個slave的話)中的一個來作為新的master,其它的slave節點會將它所追隨的master的地址改為被提升為master的slave的新地址。
很顯然,只使用單個sentinel進程來監控redis集群是不可靠的,當sentinel進程宕掉后(sentinel本身也有單點問題,single-point-of-failure)整個集群系統將無法按照預期的方式運行。所以有必要將sentinel集群:

 

 

運行sentinel有兩種方式:
redis-sentinel /path/to/sentinel.conf

redis-server /path/to/sentinel.conf --sentinel
以上兩種方式,都必須指定一個sentinel的配置文件sentinel.conf,如果不指定,將無法啟動sentinel。sentinel默認監聽26379端口,所以運行前必須確定該端口沒有被別的進程占用。
Redis源碼包中包含了一個sentinel.conf文件作為sentinel的配置文件,配置文件自帶了關於各個配置項的解釋。典型的配置項如下所示:
port 26379
工作端口,默認值為26379。
dir "/private/tmp"
工作路徑。


sentinelmonitor mymaster 127.0.0.1 6379 2
這一行代表sentinel監控的master的名字叫做mymaster,地址為127.0.0.1:6379,行尾最后的一個2代表什么意思呢?我們知道,網絡是不可靠的,有時候一個sentinel會因為網絡堵塞而誤以為一個master redis已經死掉了,當sentinel集群式,解決這個問題的方法就變得很簡單,只需要多個sentinel互相溝通來確認某個master是否真的死了,這個2代表,當集群中有2個sentinel認為master死了時,才能真正認為該master已經不可用了。(sentinel集群中各個sentinel也有互相通信,通過gossip協議)。


sentineldown-after-milliseconds mymaster 1800
sentinel會向master發送心跳PING來確認master是否存活,如果master在“一定時間范圍”內不回應PONG 或者是回復了一個錯誤消息,那么這個sentinel會主觀地(單方面地)認為這個master已經不可用了(subjectively down, 也簡稱為SDOWN)。而這個down-after-milliseconds就是用來指定這個“一定時間范圍”的,單位是毫秒。


不過需要注意的是,這個時候sentinel並不會馬上進行failover主備切換,這個sentinel還需要參考sentinel集群中其他sentinel的意見,如果超過某個數量的sentinel也主觀地認為該master死了,那么這個master就會被客觀地(這次不是主觀,是客觀,與剛才的subjectively down相對,這次是objectively down,簡稱為ODOWN)認為已經死了。需要一起做出決定的sentinel數量在上一條配置中進行配置。


sentinelfailover-timeout mymaster 36000
若sentinel在該配置值內未能完成failover操作(即故障時master/slave自動切換),則認為本次failover失敗


parallel-syncsmymaster 1
在發生failover主備切換時,這個選項指定了最多可以有多少個slave同時對新的master進行同步,這個數字越小,完成failover所需的時間就越長,但是如果這個數字越大,就意味着越多的slave因為replication而不可用。可以通過將這個值設為 1 來保證每次只有一個slave處於不能處理命令請求的狀態。
其余配置項可參考原文的注釋。
所有的配置都可以在運行時用命令SENTINEL SET command動態修改。

sentinel集群中各個sentinel都互相連接彼此來檢查對方的可用性以及互相發送消息。但是不用在任何一個sentinel配置任何其它的sentinel的節點。因為sentinel利用了master的發布/訂閱機制去自動發現其它也監控了同一master的sentinel節點。通過向名為__sentinel__:hello的管道中發送消息來實現。同樣,也不需要在sentinel中配置某個master的所有slave的地址,sentinel會通過詢問master來得到這些slave的地址的。每個sentinel通過向每個master和slave的發布/訂閱頻道__sentinel__:hello每秒發送一次消息,來宣布它的存在。每個sentinel也訂閱了每個master和slave的頻道__sentinel__:hello的內容,來發現未知的sentinel,當檢測到了新的sentinel,則將其加入到自身維護的master監控列表中。每個sentinel發送的消息中也包含了其當前維護的最新的master配置。如果某個sentinel發現。自己的配置版本低於接收到的配置版本,則會用新的配置更新自己的master配置。為一個master添加一個新的sentinel前,sentinel總是檢查是否已經有sentinel與新的sentinel的進程號或者是地址是一樣的。如果是那樣,這個sentinel將會被刪除,而把新的sentinel添加上去。
需要注意的是,配置文件在sentinel運行期間是會被動態修改的,例如當發生主備切換時候,配置文件中的master會被修改為另外一個slave。這樣,之后sentinel如果重啟時,就可以根據這個配置來恢復其之前所監控的redis集群的狀態。

failover機制

前面我們談到,當一個master被sentinel集群監控時,需要為它指定一個參數,這個參數指定了當需要判決master為不可用並且進行failover時所需要的sentinel數量,本文中我們暫時稱這個參數為票數。
不過,當failover主備切換真正被觸發后,failover並不會馬上進行,還需要sentinel中的大多數sentinel授權后才可以進行failover。
當ODOWN時,failover被觸發。failover一旦被觸發,嘗試去進行failover的sentinel會去獲得“大多數”sentinel的授權(如果票數比大多數還要大的時候,則詢問更多的sentinel)
例如,集群中有5個sentinel,票數被設置為2,當2個sentinel認為一個master已經不可用了以后,將會觸發failover,但是,進行failover的那個sentinel必須先獲得至少3個sentinel的授權才可以實行failover。
如果票數被設置為5,要達到ODOWN狀態,必須所有5個sentinel都主觀認為master為不可用,要進行failover,那么得獲得所有5個sentinel的授權。
而且,sentinel集群都遵守一個規則:如果sentinel A推薦sentinel B去執行failover,B會等待一段時間后,自行再次去對同一個master執行failover,這個等待的時間是通過failover-timeout配置項去配置的。從這個規則可以看出,sentinel集群中的sentinel不會再同一時刻並發去failover同一個master,第一個進行failover的sentinel如果失敗了,另外一個將會在一定時間內進行重新進行failover,以此類推。
(1)redis sentinel保證了活躍性:如果大多數sentinel能夠互相通信,最終將會有一個被授權去進行failover.
(2)redis sentinel也保證了安全性:每個試圖去failover同一個master的sentinel都會得到一個獨一無二的版本號。
一旦一個sentinel成功地對一個master進行了failover,它將會把關於master的最新配置通過廣播形式通知其它sentinel,其它的sentinel則更新對應master的配置。
一個faiover要想被成功實行,sentinel必須能夠向選為master的slave發送SLAVE OF NO ONE命令,然后能夠通過INFO命令看到新master的配置信息。
當將一個slave選舉為master並發送SLAVE OF NO ONE后,即使其它的slave還沒針對新master重新配置自己,failover也被認為是成功了的,然后所有sentinels將會發布新的配置信息。
新配在集群中相互傳播的方式,就是為什么我們需要當一個sentinel進行failover時必須被授權一個版本號的原因。
每個sentinel使用##發布/訂閱##的方式持續地傳播master的配置版本信息,配置傳播的##發布/訂閱##管道是:__sentinel__:hello。
因為每一個配置都有一個版本號,所以以版本號最大的那個為標准。
例如:假設有一個名為mymaster的地址為192.168.1.50:6379。一開始,集群中所有的sentinel都知道這個地址,於是為mymaster的配置打上版本號1。一段時候后mymaster死了,有一個sentinel被授權用版本號2對其進行failover。如果failover成功了,假設地址改為了192.168.1.50:9000,此時配置的版本號為2,進行failover的sentinel會將新配置廣播給其他的sentinel,由於其他sentinel維護的版本號為1,發現新配置的版本號為2時,版本號變大了,說明配置更新了,於是就會采用最新的版本號為2的配置。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM