http://www.tuicool.com/articles/beYZfi7
引言
高可用(High Available)是線上生產(chǎn)環(huán)境所必不可少的重要條件,阿里云數(shù)據(jù)庫Redis版作為一款成熟穩(wěn)定的數(shù)據(jù)庫產(chǎn)品,針對Redis的特性也支持高可用,本文將介紹云Redis是如何實現(xiàn)這一方案。
架構(gòu)
目前云Redis有主從版和集群版兩種架構(gòu),本次主要針對主從版做HA的解析,集群版HA只在最后切換VIP指向時稍有不同,但均可保證高可用性。
下圖為主從版架構(gòu):

由圖可知,云Redis實例有主備兩個節(jié)點,平時只有Master提供服務(wù),Slave只做熱備不提供訪問,Slave通過slaveof命令不斷從Master接收數(shù)據(jù),保證Master宕機時云Redis仍可提供服務(wù)。
每一個云Redis實例都會分配一個VIP并與DNS綁定,VIP經(jīng)過SLB后直接訪問Master不再有其他中間層,訪問Redis的鏈路為DNS-->VIP-->SLB-->REDIS(MASTER)。
HA模塊
HA作為一個獨立的系統(tǒng)模塊,遠(yuǎn)程探測云Redis的健康狀況,當(dāng)發(fā)生實例不可用時及時主備切換以保證服務(wù)質(zhì)量。
健康檢查
健康檢查的邏輯很簡單,通過客戶端連接Redis并發(fā)送PING命令,如果返回PONG則說明Redis健康,其他情況則說明Redis異常,檢測邏輯用偽代碼來說明:
try: client = Redis(ip, port, connection_timeout, socket_timeout) //指定要連接Redis的ip:port(這里的ip:port即可以是VIP:VPORT也可以是Master或Slave的物理ip:port,HA會有多維度的探測),并設(shè)置超時時間 client.connect() //嘗試連接Redis,如果連接失敗或超時則會拋出異常 res = client.ping() //向Redis發(fā)送ping命令,結(jié)果為PONG說明Redis健康,返回OK;結(jié)果非PONG或超時則會拋出異常 if res == PONG: return OK except: //處理異常情況,若異常在預(yù)先定義的錯誤內(nèi),說明Redis真的異常,返回ERROR,HA會做下一步切換動作 if e.message in ERRORS: return ERROR else: return OK
需要HA真正做切換的異常情況有以下幾種:
/* 指定ip地址的機器不能找到(也就是說從當(dāng)前機器不存在到指定ip路由),或者是該ip存在,但找不到指定的端口進(jìn)行監(jiān)聽,這時Redis所在主機可能宕機或是進(jìn)程掛掉 */ "Connection refused" /* 服務(wù)器的并發(fā)連接數(shù)超過了其承載量,服務(wù)器會將其中一些連接主動Down掉 */ "Connection reset" /* 連接超時,目前設(shè)置為18秒 */ "connect timed out" /* 讀取數(shù)據(jù)超時,目前設(shè)置為2分鐘 */ "Read timed out" /* Redis正在加載數(shù)據(jù) */ "LOADING Redis is loading the dataset in memory" /* 訪問的Redis是Slave */ "READONLY You can't write against a read only slave"
關(guān)于為何將讀超時設(shè)置為2分鐘這么久呢,這是我們在日常運維處理各種問題時,根據(jù)總結(jié)出來的經(jīng)驗設(shè)置的一個相對合理的大小。
在一開始時讀超時的時間設(shè)置的和連接超時同樣為18秒,結(jié)果線上經(jīng)常會有HA發(fā)生主備切換,這是因為Redis處理客戶端命令的線程只有一個,當(dāng)在處理一些耗時操作比如FLUSHALL、KEYS等命令時,執(zhí)行時間可達(dá)數(shù)十秒甚至幾分鐘,此時Redis處于"假死"狀態(tài)造成誤切換。經(jīng)測試,清空64G的數(shù)據(jù)大約需要2分鐘的時間(目前主從版云Redis最大實例規(guī)格即為64G),故將讀超時設(shè)置為2分鐘。
單純調(diào)整超時時間并不是我們的最終方案,這里仍在改進(jìn),比如增加一個狀態(tài)監(jiān)測端口,新開一個狀態(tài)線程來探測Redis活性等,歡迎大家集思廣益提供優(yōu)質(zhì)解決方案。
主備切換前準(zhǔn)備工作
當(dāng)健康檢查發(fā)現(xiàn)Redis出現(xiàn)不可用情況時就要準(zhǔn)備進(jìn)行主備切換,在主備切換真正執(zhí)行前需要額外做一些工作:
通過VIP檢查Redis健康狀態(tài) if VIP不健康: 檢查Slave狀態(tài) if Slave健康: 再次檢查VIP狀態(tài) if VIP健康: 無需主備切換 else: 執(zhí)行主備切換 else: Slave不健康無法切換 else: 無需主備切換
在執(zhí)行切換前要檢查Slave狀態(tài)以確保切換后實例是可服務(wù)的,否則即使切換也是無效的,比如兩臺主機都宕機這種極端情況。
同時對VPC類型的實例做了特殊處理,因為我們是沒有辦法訪問用戶自定義網(wǎng)絡(luò)的VIP的,這時需要把對VIP的健康檢查換成對Master的健康檢查。
執(zhí)行主備切換
當(dāng)Redis出現(xiàn)不可用且滿足切換條件時,真正開始執(zhí)行主備切換動作。同時切換動作也支持主動的任務(wù)切換和被動的故障切換,兩者主要區(qū)別在是否需要Slave等待Master達(dá)到同步狀態(tài),以下對主備切換做詳細(xì)說明:
1. 額外再次檢查一次Master狀態(tài) if Master健康: if 故障切換: 無需切換,返回成功 else: a. 設(shè)置Master為readONLY只讀狀態(tài) b. 通過info replication命令檢查主備同步狀態(tài),也即master_repl_offset是否等于Slave的offset if 若超時仍未達(dá)到一致狀態(tài): 重置Master為readwrite可讀寫狀態(tài),并返回異常 else: 日志記錄此時Master無法連接 2. 切換VIP指向Slave if 切換失敗: 記錄日志并返回異常 3. 更新主備元信息 4. 向Slave發(fā)送slaveof no one命令,使其升級為新的Master 5. 嘗試向原Master發(fā)送slaveof命令,使其降級為新的Slave 此時原Master可能已經(jīng)宕機,故不做失敗處理,僅記錄日志
通過以上方案,云數(shù)據(jù)庫Redis版SLA可以達(dá)到99.99%,僅在主從都發(fā)生宕機的極端情況無法服務(wù)。
結(jié)束
本文介紹了云數(shù)據(jù)Redis版HA方案,通過主從雙機熱備來保證服務(wù)高可用,健康檢查出現(xiàn)異常時及時進(jìn)行主備切換,有效保障業(yè)務(wù)運行。