Redis入門指南之哨兵
Redis復制的原理和使用方式,在一主多從的Redis系統(tǒng)中,從數(shù)據(jù)庫在整個系統(tǒng)中起到了數(shù)據(jù)冗余備份和讀寫分離的作用。
當主數(shù)據(jù)庫遇到異常中斷服務后,開發(fā)者可以通過手動的方式選擇一個從數(shù)據(jù)庫來升格為主數(shù)據(jù)庫,以使得系統(tǒng)能夠繼續(xù)提供服務。然而整個過程相對麻煩且需要人工介入。 為此,Redis 2.8中提供了哨兵工具來實現(xiàn)自動化的系統(tǒng)監(jiān)控和故障恢復功能。
哨兵的作用就是監(jiān)控Redis系統(tǒng)的運行狀況。它的功能包括以下兩個。
1.監(jiān)控主數(shù)據(jù)庫和從數(shù)據(jù)庫是否正常運行。?
2.主數(shù)據(jù)庫出現(xiàn)故障時自動將從數(shù)據(jù)庫轉換為主數(shù)據(jù)庫。
在一主多從的Redis系統(tǒng)中,可以使用多個哨兵進行監(jiān)控任務以保證系統(tǒng)足夠穩(wěn)健,此時不僅哨兵會同時監(jiān)控主數(shù)據(jù)庫和從數(shù)據(jù)庫,哨兵之間也會互相監(jiān)控。
配置哨兵
[root@TA30-53?redis_master]#?vi?sentinel.conf
sentinel?monitor?mymaster?127.0.0.1?6379?2 sentinel?auth-pass?mymaster?123456
其中mymaster表示要監(jiān)控的主數(shù)據(jù)庫的名字,可以自定義。這個名字必須僅由大小寫字母、數(shù)字和“.-_”這 3 個字符組成。
后兩個參數(shù)表示主數(shù)據(jù)庫的地址和端口。
最后的1表示最低通過票數(shù)。
接下來執(zhí)行來啟動Sentinel進程,并將上述配置文件的路徑傳遞給哨兵:
[root@TA30-53?local]#?cd?redis_master/src/ [root@TA30-53?src]#?./redis-sentinel?../sentinel.conf
?配置哨兵監(jiān)控一個系統(tǒng)時,只需要配置其監(jiān)控主數(shù)據(jù)庫即可,哨兵會自動發(fā)現(xiàn)所有復制該主數(shù)據(jù)庫的從數(shù)據(jù)庫。?
啟動哨兵后,哨兵輸出如下內容:
[root@TA30-53?redis_master]#?./src/redis-sentinel?./sentinel.conf?? 30327:X?26?Jun?16:49:08.340?*?Increased?maximum?number?of?open?files?to?10032?(it?was?originally?set?to?1024). ????????????????_._?????????????????????????????????????????????????? ???????????_.-``__?''-._????????????????????????????????????????????? ??????_.-``????`.??`_.??''-._???????????Redis?3.0.7?(00000000/0)?64?bit ??.-``?.-```.??```/????_.,_?''-._??????????????????????????????????? ?(????'??????,???????.-`??|?`,????)?????Running?in?sentinel?mode ?|`-._`-...-`?__...-.``-._|'`?_.-'|?????Port:?26379 ?|????`-._???`._????/?????_.-'????|?????PID:?30327 ??`-._????`-._??`-./??_.-'????_.-'??????????????????????????????????? ?|`-._`-._????`-.__.-'????_.-'_.-'|?????????????????????????????????? ?|????`-._`-._????????_.-'_.-'????|???????????http://redis.io???????? ??`-._????`-._`-.__.-'_.-'????_.-'??????????????????????????????????? ?|`-._`-._????`-.__.-'????_.-'_.-'|?????????????????????????????????? ?|????`-._`-._????????_.-'_.-'????|?????????????????????????????????? ??`-._????`-._`-.__.-'_.-'????_.-'??????????????????????????????????? ??????`-._????`-.__.-'????_.-'??????????????????????????????????????? ??????????`-._????????_.-'??????????????????????????????????????????? ??????????????`-.__.-'??????????????????????????????????????????????? 30327:X?26?Jun?16:49:08.342?#?WARNING:?The?TCP?backlog?setting?of?511?cannot?be?enforced?because?/proc/sys/net/core/somaxconn?is?set?to?the?lower?value?of?128. 30327:X?26?Jun?16:49:08.342?#?Sentinel?runid?is?9bcfa5e06a5218120fb698fb41fce1cc7dc41251 30327:X?26?Jun?16:49:08.342?#?+monitor?master?mymaster?127.0.0.1?6379?quorum?1 30327:X?26?Jun?16:49:08.353?*?+slave?slave?127.0.0.1:6380?127.0.0.1?6380?@?mymaster?127.0.0.1?6379 30327:X?26?Jun?16:49:08.354?*?+slave?slave?127.0.0.1:6381?127.0.0.1?6381?@?mymaster?127.0.0.1?6379
其中+slave 表示新發(fā)現(xiàn)了從數(shù)據(jù)庫。
現(xiàn)在哨兵已經(jīng)在監(jiān)控這3個Redis實例了,這時我們將主數(shù)據(jù)庫(即運行在6379端口上的Redis實例)關閉,等待指定時間后(可以配置,默認為 30 秒),哨兵會輸出如下內容:
30553:X?26?Jun?16:58:52.100?#?+sdown?master?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:52.100?#?+odown?master?mymaster?127.0.0.1?6379?#quorum?1/1
+sdown表示哨兵主觀認為主數(shù)據(jù)庫停止服務了,+odown則表示哨兵客觀認為主數(shù)據(jù)庫停止服務了。
此時哨兵開始執(zhí)行故障恢復,即挑選一個從數(shù)據(jù)庫,將其升格為主數(shù)據(jù)庫。同時輸出如下內容:
30553:X?26?Jun?16:57:45.646?#?Sentinel?runid?is?cbf8017edb7e379fef7d2984be1d2a22c95f7835 30553:X?26?Jun?16:57:45.646?#?+monitor?master?mymaster?127.0.0.1?6379?quorum?1 30553:X?26?Jun?16:58:52.100?#?+sdown?master?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:52.100?#?+odown?master?mymaster?127.0.0.1?6379?#quorum?1/1 30553:X?26?Jun?16:58:52.100?#?+new-epoch?1 30553:X?26?Jun?16:58:52.100?#?+try-failover?master?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:52.109?#?+vote-for-leader?cbf8017edb7e379fef7d2984be1d2a22c95f7835?1 30553:X?26?Jun?16:58:52.109?#?+elected-leader?master?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:52.109?#?+failover-state-select-slave?master?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:52.171?#?+selected-slave?slave?127.0.0.1:6381?127.0.0.1?6381?@?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:52.171?*?+failover-state-send-slaveof-noone?slave?127.0.0.1:6381?127.0.0.1?6381?@?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:52.243?*?+failover-state-wait-promotion?slave?127.0.0.1:6381?127.0.0.1?6381?@?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:53.163?#?+promoted-slave?slave?127.0.0.1:6381?127.0.0.1?6381?@?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:53.163?#?+failover-state-reconf-slaves?master?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:53.246?*?+slave-reconf-sent?slave?127.0.0.1:6380?127.0.0.1?6380?@?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:54.211?*?+slave-reconf-inprog?slave?127.0.0.1:6380?127.0.0.1?6380?@?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:55.240?*?+slave-reconf-done?slave?127.0.0.1:6380?127.0.0.1?6380?@?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:55.331?#?+failover-end?master?mymaster?127.0.0.1?6379 30553:X?26?Jun?16:58:55.331?#?+switch-master?mymaster?127.0.0.1?6379?127.0.0.1?6381 30553:X?26?Jun?16:58:55.331?*?+slave?slave?127.0.0.1:6380?127.0.0.1?6380?@?mymaster?127.0.0.1?6381 30553:X?26?Jun?16:58:55.331?*?+slave?slave?127.0.0.1:6379?127.0.0.1?6379?@?mymaster?127.0.0.1?6381 30553:X?26?Jun?16:59:25.352?#?+sdown?slave?127.0.0.1:6379?127.0.0.1?6379?@?mymaster?127.0.0.1?6381
+try-failover表示哨兵開始進行故障恢復,
+failover-end表示哨兵完成故障恢復,期間涉及領頭哨兵的選舉、備選從數(shù)據(jù)庫的選擇等
+switch-master表示主數(shù)據(jù)庫從6379遷移到6381,即6381的從數(shù)據(jù)庫被升為主數(shù)據(jù)庫,同時兩個+slave則列出了新的主數(shù)據(jù)庫的兩個從數(shù)據(jù)庫,為6380和6379。
6379就是之前停止服務的主數(shù)據(jù)庫,停止服務了,而6381端口的從數(shù)據(jù)庫已經(jīng)升為主數(shù)據(jù)庫,當6379端口的實例恢復服務后,會轉變?yōu)?381端口實例的從數(shù)據(jù)庫來運行,所以哨兵將6379端口實例的信息修改成了6381端口實例的從數(shù)據(jù)庫。
故障恢復完成后,可以使用Redis命令行客戶端重新檢查6380和6381兩個端口上的實例的復制信息:
[root@TA30-53?local]#?./redis_slave1/src/redis-cli?-p?6380?-a?123456 127.0.0.1:6380>?info?replication #?Replication role:slave master_host:127.0.0.1 master_port:6381 master_link_status:up master_last_io_seconds_ago:2 master_sync_in_progress:0 slave_repl_offset:54239 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
[root@TA30-53?redis_slave2]#?./src/redis-cli?-p?6381?-a?123456?info?replication #?Replication role:master connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=57634,lag=1 master_repl_offset:57767 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:57766
可以看到6381端口上的實例已經(jīng)確實升格為主數(shù)據(jù)庫了,同時6380端口上的實例是其從數(shù)據(jù)庫。整個故障恢復過程就此完成。
此時將6379端口上的實例重新啟動,
[root@TA30-53?local]#?./redis_master/src/redis-server?./redis_master/redis.conf
哨兵會監(jiān)控到這一變化,并輸出:
30553:X?26?Jun?17:17:07.568?#?-sdown?slave?127.0.0.1:6379?127.0.0.1?6379?@?mymaster?127.0.0.1?6381 30553:X?26?Jun?17:17:17.528?*?+convert-to-slave?slave?127.0.0.1:6379?127.0.0.1?6379?@?mymaster?127.0.0.1?6381
-sdown表示實例6379已經(jīng)恢復服務了(與+sdown相反),
+convert-to-slave表示將6379端口的實例設置為6381端口實例的從數(shù)據(jù)庫。
這時使用Redis命令行客戶端查看6379端口實例的復制信息為:
[root@TA30-53?local]#?./redis_master/src/redis-cli?-p?6379?-a?123456?info?replication???????????? #?Replication role:slave master_host:127.0.0.1 master_port:6381 master_link_status:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 slave_repl_offset:1 master_link_down_since_seconds:1498469302 slave_priority:100 slave_read_only:1 connected_slaves:0 min_slaves_good_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
同時6381端口實例的復制信息為:
127.0.0.1:6380>?info?replication #?Replication role:master connected_slaves:1 slave0:ip=127.0.0.1,port=6381,state=online,offset=8683,lag=0 master_repl_offset:8683 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:8682
為什么不是倆,后面再研究。。。。
原理
一個哨兵進程啟動時會讀取配置文件的內容,通過如下的配置找出需要監(jiān)控的主數(shù)據(jù)庫:
?sentinel monitor master-name ip redis-port quorum?
其中 master-name 是一個由大小寫字母、數(shù)字和“.-_”組成的主數(shù)據(jù)庫的名字,
考慮到故障恢復后當前監(jiān)控的系統(tǒng)的主數(shù)據(jù)庫的地址和端口會產(chǎn)生變化,所以哨兵提供了命令可以通過主數(shù)據(jù)庫的名字獲取當前系統(tǒng)的主數(shù)據(jù)庫的地址和端口號。
ip 表示當前系統(tǒng)中主數(shù)據(jù)庫的地址,redis-port 表示端口號,quorum用來表示執(zhí)行故障恢復操作前至少需要幾個哨兵節(jié)點同意。
一個哨兵節(jié)點可以同時監(jiān)控多個Redis主從系統(tǒng),只需要提供多個sentinel monitor配置即可,例如:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel monitor othermaster 192.168.1.3 6380 4
同時多個哨兵節(jié)點也可以同時監(jiān)控同一個 Redis 主從系統(tǒng),從而形成網(wǎng)狀結構。
哨兵啟動后,會與要監(jiān)控的主數(shù)據(jù)庫建立兩條連接,這兩個連接的建立方式與普通的Redis客戶端無異。其中一條連接用來訂閱該主數(shù)據(jù)的__sentinel__:hello頻道以獲取其他同樣監(jiān)控該數(shù)據(jù)庫的哨兵節(jié)點的信息,另外哨兵也需要定期向主數(shù)據(jù)庫發(fā)送 INFO 等命令來獲取主數(shù)據(jù)庫本身的信息
和主數(shù)據(jù)庫的連接建立完成后,哨兵會定時執(zhí)行下面3個操作。 (1)每10秒哨兵會向主數(shù)據(jù)庫和從數(shù)據(jù)庫發(fā)送INFO命令。 (2)每 2 秒哨兵會向主數(shù)據(jù)庫和從數(shù)據(jù)庫的__sentinel__:hello 頻道發(fā)送自己的信息。 (3)每1秒哨兵會向主數(shù)據(jù)庫、從數(shù)據(jù)庫和其他哨兵節(jié)點發(fā)送PING命令。
首先,發(fā)送INFO命令使得哨兵可以獲得當前數(shù)據(jù)庫的相關信息(包括運行ID、復制信息等)從而實現(xiàn)新節(jié)點的自動發(fā)現(xiàn)。
配置哨兵監(jiān)控 Redis 主從系統(tǒng)時只需要指定主數(shù)據(jù)庫的信息即可,哨兵借助 INFO 命令來獲取所有復制該主數(shù)據(jù)庫的從數(shù)據(jù)庫信息的。
啟動后,哨兵向主數(shù)據(jù)庫發(fā)送 INFO 命令,通過解析返回結果來得知從數(shù)據(jù)庫列表,而后對每個從數(shù)據(jù)庫同樣建立兩個連接,兩個連接的作用和前文介紹的與主數(shù)據(jù)庫建立的兩個連接完全一致。在此之后,哨兵會每 10 秒定時向已知的所有主從數(shù)據(jù)庫發(fā)送INFO命令來獲取信息更新并進行相應操作,比如對新增的從數(shù)據(jù)庫建立連接并加入監(jiān)控列表,對主從數(shù)據(jù)庫的角色變化(由故障恢復操作引起)進行信息更新等。
接下來哨兵向主從數(shù)據(jù)庫的__sentinel__:hello 頻道發(fā)送信息來與同樣監(jiān)控該數(shù)據(jù)庫的哨兵分享自己的信息。
發(fā)送的消息內容為:
可以看到消息包括的哨兵的基本信息,以及其監(jiān)控的主數(shù)據(jù)庫的信息。哨兵會訂閱每個其監(jiān)控的數(shù)據(jù)庫的__sentinel__:hello頻道,所以當其他哨兵收到消息后,會判斷發(fā)消息的哨兵是不是新發(fā)現(xiàn)的哨兵。如果是則將其加入已發(fā)現(xiàn)的哨兵列表中并創(chuàng)建一個到其的連接(與數(shù)據(jù)庫不同,哨兵與哨兵之間只會創(chuàng)建一條連接用來發(fā)送 PING命令,而不需要創(chuàng)建另外一條連接來訂閱頻道,因為哨兵只需要訂閱數(shù)據(jù)庫的頻道即可實現(xiàn)自動發(fā)現(xiàn)其他哨兵)。同時哨兵會判斷信息中主數(shù)據(jù)庫的配置版本,如果該版本比當前記錄的主數(shù)據(jù)庫的版本高,則更新主數(shù)據(jù)庫的數(shù)據(jù)。
實現(xiàn)了自動發(fā)現(xiàn)從數(shù)據(jù)庫和其他哨兵節(jié)點后,哨兵要做的就是定時監(jiān)控這些數(shù)據(jù)庫和節(jié)點有沒有停止服務。
每隔一定時間向這些節(jié)點發(fā)送PING命令。
時間間隔與down-after-milliseconds選項有關,當down-after-milliseconds的值小于1秒時,哨兵會每隔down-after-milliseconds指定的時間發(fā)送一次PING命令,
當down-after-milliseconds的值大于1秒時,哨兵會每隔1秒發(fā)送一次PING命令。
例如:?
//每隔 1 秒發(fā)送一次 PING命令?
sentinel down-after-milliseconds mymaster 60000?
//每隔 600 毫秒發(fā)送一次 PING命令?
sentinel down-after-milliseconds othermaster 600?
當超過down-after-milliseconds選項指定時間后,如果被PING的數(shù)據(jù)庫或節(jié)點仍然未進行回復,則哨兵認為其主觀下線(subjectively down)。
主觀下線表示從當前的哨兵進程看來,該節(jié)點已經(jīng)下線。
如果該節(jié)點是主數(shù)據(jù)庫,則哨兵會進一步判斷是否需要對其進行故障恢復:哨兵發(fā)送 SENTINEL is-master-down-by-addr命令詢問其他哨兵節(jié)點以了解他們是否也認為該主數(shù)據(jù)庫主觀下線,如果達到指定數(shù)量時,哨兵會認為其客觀下線(objectively down),并選舉領頭的哨兵節(jié)點對主從系統(tǒng)發(fā)起故障恢復。這個指定數(shù)量即為quorum參數(shù)。
例如,
sentinel monitor mymaster 127.0.0.1 6379 2
該配置表示只有當至少兩個 Sentinel 節(jié)點(包括當前節(jié)點)認為該主數(shù)據(jù)庫主觀下線時,當前哨兵節(jié)點才會認為該主數(shù)據(jù)庫客觀下線。
進行接下來的選舉領頭哨兵步驟。
雖然當前哨兵節(jié)點發(fā)現(xiàn)了主數(shù)據(jù)庫客觀下線,需要故障恢復,但是故障恢復需要由領頭的哨兵來完成,這樣可以保證同一時間只有一個哨兵節(jié)點來執(zhí)行故障恢復。
選舉領頭哨兵的過程使用了Raft算法,具體過程如下。
(1)發(fā)現(xiàn)主數(shù)據(jù)庫客觀下線的哨兵節(jié)點(下面稱作A)向每個哨兵節(jié)點發(fā)送命令,要求對方選自己成為領頭哨兵。
(2)如果目標哨兵節(jié)點沒有選過其他人,則會同意將A設置成領頭哨兵。
(3)如果A發(fā)現(xiàn)有超過半數(shù)且超過quorum參數(shù)值的哨兵節(jié)點同意選自己成為領頭哨兵,則A成功成為領頭哨兵。
(4)當有多個哨兵節(jié)點同時參選領頭哨兵,則會出現(xiàn)沒有任何節(jié)點當選的可能。此時每個參選節(jié)點將等待一個隨機時間重新發(fā)起參選請求,進行下一輪選舉,直到選舉成功。
選出領頭哨兵后,領頭哨兵將會開始對主數(shù)據(jù)庫進行故障恢復。
故障恢復的過程相如下。
?首先領頭哨兵將從停止服務的主數(shù)據(jù)庫的從數(shù)據(jù)庫中挑選一個來充當新的主數(shù)據(jù)庫。挑選的依據(jù)如下。
(1)所有在線的從數(shù)據(jù)庫中,選擇優(yōu)先級最高的從數(shù)據(jù)庫。優(yōu)先級可以通過slave-priority選項來設置。
(2)如果有多個最高優(yōu)先級的從數(shù)據(jù)庫,則復制的命令偏移量越大(即復制越完整)越優(yōu)先。
(3)如果以上條件都一樣,則選擇運行ID較小的從數(shù)據(jù)庫。
選出一個從數(shù)據(jù)庫后,領頭哨兵將向從數(shù)據(jù)庫發(fā)送 SLAVEOF NO ONE命令使其升格為主數(shù)據(jù)庫。
而后領頭哨兵向其他從數(shù)據(jù)庫發(fā)送SLAVEOF命令來使其成為新主數(shù)據(jù)庫的從數(shù)據(jù)庫。
最后一步則是更新內部的記錄,將已經(jīng)停止服務的舊的主數(shù)據(jù)庫更新為新的主數(shù)據(jù)庫的從數(shù)據(jù)庫,使得當其恢復服務時自動以從數(shù)據(jù)庫的身份繼續(xù)服務。
哨兵的部署
哨兵以獨立進程的方式對一個主從系統(tǒng)進行監(jiān)控,監(jiān)控的效果好壞與否取決于哨兵的視角是否有代表性。
如果一個主從系統(tǒng)中配置的哨兵較少,哨兵對整個系統(tǒng)的判斷的可靠性就會降低。
整體來講,相對穩(wěn)妥的哨兵部署方案是使得哨兵的視角盡可能地與每個節(jié)點的視角一致,即:
(1)為每個節(jié)點(無論是主數(shù)據(jù)庫還是從數(shù)據(jù)庫)部署一個哨兵;
(2)使每個哨兵與其對應的節(jié)點的網(wǎng)絡環(huán)境相同或相近。?
這樣的部署方案可以保證哨兵的視角擁有較高的代表性和可靠性。