12.【Redis系列】集群方案1- Sentinel

目前我們講的 Redis 還只是主從方案,最終一致性。讀者們可思考過,如果主節(jié)點凌晨 3 點突發(fā)宕機怎么辦?就坐等運維從床上爬起來,然后手工進行從主切換,再通知所有的程序把地址統(tǒng)統(tǒng)改一遍重新上線么?毫無疑問,這樣的人工運維效率太低,事故發(fā)生時估計得至少 1 個小時才能緩過來。如果是一個大型公司,這樣的事故足以上新聞了。

所以我們必須有一個高可用方案來抵抗節(jié)點故障,當故障發(fā)生時可以自動進行從主切換,程序可以不用重啟,運維可以繼續(xù)睡大覺,仿佛什么事也沒發(fā)生一樣。Redis 官方提供了這樣一種方案 —— Redis Sentinel(哨兵)。

image.png

我們可以將 Redis Sentinel 集群看成是一個 ZooKeeper 集群,它是集群高可用的心臟,它一般是由 3~5 個節(jié)點組成,這樣掛了個別節(jié)點集群還可以正常運轉(zhuǎn)。

它負責持續(xù)監(jiān)控主從節(jié)點的健康,當主節(jié)點掛掉時,自動選擇一個最優(yōu)的從節(jié)點切換為主節(jié)點。客戶端來連接集群時,會首先連接 sentinel,通過 sentinel 來查詢主節(jié)點的地址,然后再去連接主節(jié)點進行數(shù)據(jù)交互。當主節(jié)點發(fā)生故障時,客戶端會重新向 sentinel 要地址,sentinel 會將最新的主節(jié)點地址告訴客戶端。如此應(yīng)用程序?qū)o需重啟即可自動完成節(jié)點切換。比如上圖的主節(jié)點掛掉后,集群將可能自動調(diào)整為下圖所示結(jié)構(gòu)。

image.png

從這張圖中我們能看到主節(jié)點掛掉了,原先的主從復(fù)制也斷開了,客戶端和損壞的主節(jié)點也斷開了。從節(jié)點被提升為新的主節(jié)點,其它從節(jié)點開始和新的主節(jié)點建立復(fù)制關(guān)系??蛻舳送ㄟ^新的主節(jié)點繼續(xù)進行交互。Sentinel 會持續(xù)監(jiān)控已經(jīng)掛掉了主節(jié)點,待它恢復(fù)后,集群會調(diào)整為下面這張圖。

image.png

此時原先掛掉的主節(jié)點現(xiàn)在變成了從節(jié)點,從新的主節(jié)點那里建立復(fù)制關(guān)系。

消息丟失
Redis 主從采用異步復(fù)制,意味著當主節(jié)點掛掉時,從節(jié)點可能沒有收到全部的同步消息,這部分未同步的消息就丟失了。如果主從延遲特別大,那么丟失的數(shù)據(jù)就可能會特別多。Sentinel 無法保證消息完全不丟失,但是也盡可能保證消息少丟失。它有兩個選項可以限制主從延遲過大。

min-slaves-to-write 1
min-slaves-max-lag 10
第一個參數(shù)表示主節(jié)點必須至少有一個從節(jié)點在進行正常復(fù)制,否則就停止對外寫服務(wù),喪失可用性。

何為正常復(fù)制,何為異常復(fù)制?這個就是由第二個參數(shù)控制的,它的單位是秒,表示如果 10s 沒有收到從節(jié)點的反饋,就意味著從節(jié)點同步不正常,要么網(wǎng)絡(luò)斷開了,要么一直沒有給反饋。

Sentinel 基本使用
接下來我們看看客戶端如何使用 sentinel,標準的流程應(yīng)該是客戶端可以通過 sentinel 發(fā)現(xiàn)主從節(jié)點的地址,然后在通過這些地址建立相應(yīng)的連接來進行數(shù)據(jù)存取操作。我們來看看 Python 客戶端是如何做的。

>>> from redis.sentinel import Sentinel
>>>sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1)
>>>sentinel.discover_master('mymaster')
('127.0.0.1', 6379)
>>> sentinel.discover_slaves('mymaster')
[('127.0.0.1', 6380)]

sentinel 的默認端口是 26379,不同于 Redis 的默認端口 6379,通過 sentinel 對象的 discover_xxx 方法可以發(fā)現(xiàn)主從地址,主地址只有一個,從地址可以有多個。

>>> master = sentinel.master_for('mymaster', socket_timeout=0.1)
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
>>> master.set('foo', 'bar')
>>> slave.get('foo')
'bar'

通過 xxx_for 方法可以從連接池中拿出一個連接來使用,因為從地址有多個,redis 客戶端對從地址采用輪詢方案,也就是 RoundRobin 輪著來。

有個問題是,但 sentinel 進行主從切換時,客戶端如何知道地址變更了 ? 通過分析源碼,我發(fā)現(xiàn) redis-py 在建立連接的時候進行了主庫地址變更判斷。

連接池建立新連接時,會去查詢主庫地址,然后跟內(nèi)存中的主庫地址進行比對,如果變更了,就斷開所有連接,重新使用新地址建立新連接。如果是舊的主庫掛掉了,那么所有正在使用的連接都會被關(guān)閉,然后在重連時就會用上新地址。

但是這樣還不夠,如果是 sentinel 主動進行主從切換,主庫并沒有掛掉,而之前的主庫連接已經(jīng)建立了在使用了,沒有新連接需要建立,那這個連接是不是一致切換不了?

繼續(xù)深入研究源碼,我發(fā)現(xiàn) redis-py 在另外一個點也做了控制。那就是在處理命令的時候捕獲了一個特殊的異常ReadOnlyError,在這個異常里將所有的舊連接全部關(guān)閉了,后續(xù)指令就會進行重連。

主從切換后,之前的主庫被降級到從庫,所有的修改性的指令都會拋出ReadonlyError。如果沒有修改性指令,雖然連接不會得到切換,但是數(shù)據(jù)不會被破壞,所以即使不切換也沒關(guān)系。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Redis Sentinel 簡介 redis 的主從復(fù)制模式下,一旦主節(jié)點由于故障不能提供服務(wù),需要人工將...
    CoderJed閱讀 2,944評論 0 11
  • 前言 Redis是一個高性能的key-value數(shù)據(jù)庫,現(xiàn)時越來越多企業(yè)與應(yīng)用使用Redis作為緩存服務(wù)器。樓主是...
    liangzzz閱讀 4,433評論 9 152
  • 單機/單點 單點故障/瓶頸:多個節(jié)點負載:面向數(shù)據(jù):一變多(一致性<弱一致,最終一致性>)》可用性最終一致性:一部...
    壹點零閱讀 858評論 0 3
  • 經(jīng)濟學原理 宏觀經(jīng)濟學 讀書報告 繼讀了微觀經(jīng)濟學后再大家的陪同下又啃下了,宏觀經(jīng)濟學這一部“著作”感覺心里有...
    楒月閱讀 188評論 0 0
  • 周三晚上,我到他家去玩。許久不見,他已經(jīng)在我們老家這里買房子了。房子離他父母家很近,只隔著三幢住宅樓。“這是新開發(fā)...
    卡貝田閱讀 359評論 0 0

友情鏈接更多精彩內(nèi)容