一、背景和概況
在2015年末,為了解決各類業(yè)務(wù)大量的排行榜的需求,我們基于redis實(shí)現(xiàn)了一個(gè)通用的排行榜服務(wù),良好解決了各類業(yè)務(wù)的痛點(diǎn),但是隨著業(yè)務(wù)發(fā)展到2016年中,其中一個(gè)業(yè)務(wù)就申請(qǐng)了數(shù)百萬排行榜,并隨著增長趨勢(shì),破千萬指日可待,同時(shí)各業(yè)務(wù)也希望能直接使用redis豐富數(shù)據(jù)結(jié)構(gòu)來解決更多問題(如存儲(chǔ)關(guān)關(guān)系鏈、地理位置等)。
當(dāng)時(shí)面臨的問題與挑戰(zhàn)如下:
-
排行榜服務(wù)無法支撐千萬級(jí)排行榜數(shù)(排行榜到redis實(shí)例映射關(guān)系存儲(chǔ)在zookeeper,zookeeper容量瓶頸)
-
單機(jī)容量無法滿足關(guān)系鏈等業(yè)務(wù)需求
-
排行榜和關(guān)系鏈大部分大于1M,同時(shí)存在超大key(>512M),需支持超大key遷移
-
SNG的Grocery存儲(chǔ)組件,支持redis協(xié)議,但又存在單key value大小1M的限制
-
高可用,需要支持redis主備自動(dòng)切換
-
SNG數(shù)據(jù)運(yùn)維組未提供redis集群版接入服務(wù),在零運(yùn)維的支持下如何高效治理眾多業(yè)務(wù)集群?
面對(duì)以上挑戰(zhàn),經(jīng)過多維度的方案選型對(duì)比,最終選擇了基于codis(3.x版本),結(jié)合內(nèi)部需求和運(yùn)營環(huán)境進(jìn)行了定制化改造,截止到目前,初步實(shí)現(xiàn)了一個(gè)支持單機(jī)/分布式存儲(chǔ)、平滑擴(kuò)縮容、超大key遷移、高可用、業(yè)務(wù)自動(dòng)化接入調(diào)度部署、多維度監(jiān)控、配置管理、容量管理、運(yùn)營統(tǒng)計(jì)的redis服務(wù)平臺(tái),在CMEM、Grocery不能滿足業(yè)務(wù)需求的場景下,接入了SNG增值產(chǎn)品部等五大部門近30+業(yè)務(wù)集群(圖一),350+實(shí)例, 2T+容量。
下文將從方案選型、整體架構(gòu)、自動(dòng)化接入、數(shù)據(jù)遷移、高可用、運(yùn)營實(shí)踐等方面詳細(xì)介紹我們?cè)谏a(chǎn)環(huán)境中的實(shí)踐情況。
圖一 部分接入業(yè)務(wù)列表
二、方案選型
Redis因其豐富的數(shù)據(jù)結(jié)構(gòu)、易用性越來越受到廣大開發(fā)者歡迎,根據(jù)DB-Engines的最新統(tǒng)計(jì),已經(jīng)是穩(wěn)居數(shù)據(jù)庫產(chǎn)品的top10(見圖一)。
云計(jì)算服務(wù)產(chǎn)商AWS、AZURE、阿里云、騰訊云都提供了Redis產(chǎn)品,各云計(jì)算產(chǎn)商主流方案都是基于開源Redis內(nèi)核做定制化優(yōu)化,解決Redis不足之處,在提升Redis穩(wěn)定性、性能的同時(shí)最大程度兼容開源Redis。單機(jī)主備版各廠商差異不大,都是基于原生Redis內(nèi)核,但是集群版,AWS使用的原生的Redis自帶的Cluster Mode模式(加強(qiáng)版),,阿里云基于Proxy、原生Redis內(nèi)核實(shí)現(xiàn),路由等元存儲(chǔ)數(shù)據(jù)保存在RDS,架構(gòu)類似Codis, 騰訊云集群版是基于內(nèi)部Grocery。了解完云計(jì)算產(chǎn)商解決方案,再看業(yè)界開源、公司內(nèi)部,上文提到我們面臨問題之一就是單機(jī)容量瓶頸,因此需要一款集群版產(chǎn)品,目前業(yè)界開源的主流的Redis集群解決方案有Codis,Redis Cluster,Twemproxy,公司內(nèi)部的有SNG Grocery、IEG的TRedis,從以下幾個(gè)維度進(jìn)行對(duì)比,詳細(xì)結(jié)果如表一所示(2016年10月時(shí)數(shù)據(jù)):

圖二 數(shù)據(jù)庫流行度排名
表一 Redis集群產(chǎn)品對(duì)比
云計(jì)算產(chǎn)商和業(yè)界開源、公司內(nèi)部的解決方案從整體架構(gòu)分類,分別是基于Proxy中心節(jié)點(diǎn)和無中心節(jié)點(diǎn),在這點(diǎn)上我們更偏愛基于Proxy中心節(jié)點(diǎn)架構(gòu)設(shè)計(jì),運(yùn)維成本更低、更加可控,從存儲(chǔ)引擎分類,分別是基于原生Redis內(nèi)核和第三方存儲(chǔ)引擎(如Grocery的多階HASH+LinkTable、TRedis的Rocksdb),在這點(diǎn)上我們更偏愛基于原生Redis內(nèi)核,因?yàn)槲覀円鉀Q業(yè)務(wù)場景就是Grocery和CMem無法滿足的地方,我們業(yè)務(wù)大部分使用的數(shù)據(jù)結(jié)構(gòu)是ZSET且Key一般超過1M,幾十萬級(jí)元素的ZSET Key是常態(tài),Grocery的Value 1M大小限制無法滿足我們的需求,同時(shí)我們需要ZSET的ZRank的時(shí)間復(fù)雜度是O(LogN),基于RocksDb的存儲(chǔ)引擎時(shí)間復(fù)雜度是O(N),因此這也是無法接受的。隨著業(yè)務(wù)發(fā)展,容量勢(shì)必會(huì)發(fā)生變化,因此擴(kuò)縮容是常態(tài),而TwemProxy并不支持平滑擴(kuò)縮容,因此也無法滿足要求。最后,我們需要結(jié)合內(nèi)部運(yùn)營環(huán)境和需求做定制化改造,在零運(yùn)維的支持下,通過技術(shù)手段,最大程度自動(dòng)化治理、運(yùn)營眾多多業(yè)務(wù)集群,而Codis代碼結(jié)構(gòu)清晰,開發(fā)語言又是現(xiàn)在比較流行的Go,無論是運(yùn)行性能、還是開發(fā)效率都較高效,因此我們最終選擇了Codis.
三、整體架構(gòu)
基于Codis定制開發(fā)而成的Redis服務(wù)平臺(tái)整體架構(gòu)如圖二所示,其包含以下組件:
-
Proxy:實(shí)現(xiàn)了Redis協(xié)議,除少數(shù)命令不支持外,對(duì)外表現(xiàn)和原生Redis一樣。解析請(qǐng)求時(shí),計(jì)算key對(duì)應(yīng)的哈希槽,將請(qǐng)求分發(fā)到對(duì)應(yīng)的Redis,業(yè)務(wù)通過L5/CMLB進(jìn)行尋址。
-
Redis: Redis在內(nèi)存中實(shí)現(xiàn)了string/list/hash/set/zset等數(shù)據(jù)結(jié)構(gòu),對(duì)外提供數(shù)據(jù)讀寫服務(wù)、持久化等,默認(rèn)一主一備部署。
-
DashbOArd:提供管理集群的API和訪問元數(shù)據(jù)存儲(chǔ)的通用API(CURD操作,屏蔽后端元數(shù)據(jù)存儲(chǔ)差異)。
-
Zk/Etcd/Mysql:第三方元數(shù)據(jù)存儲(chǔ),保存集群的proxy、redis、各哈希槽對(duì)應(yīng)的redis 地址等信息。
-
HA:基于Redis Sentinel實(shí)現(xiàn)Redis主備高可用,部署在多個(gè)IDC,采用Quorum機(jī)制、狀態(tài)機(jī)進(jìn)行主備自動(dòng)切換。
-
Scheduler:調(diào)度服務(wù),負(fù)責(zé)對(duì)業(yè)務(wù)接入申請(qǐng)單進(jìn)行自動(dòng)調(diào)度部署、集群自動(dòng)化擴(kuò)容、各集群運(yùn)營數(shù)據(jù)統(tǒng)計(jì)等。
-
運(yùn)維管理系統(tǒng): Web可視化管理集群,提供業(yè)務(wù)接入、集群管理、容量管理、配置管理等功能。
-
CDB:存儲(chǔ)業(yè)務(wù)申請(qǐng)單、各節(jié)點(diǎn)容量等信息。
-
Agent:負(fù)責(zé)定時(shí)監(jiān)控和采集Redis、Proxy、Dashboard運(yùn)行統(tǒng)計(jì)信息,上報(bào)到米格監(jiān)控系統(tǒng)和CDB。
-
HDFS:冷備集群,Redis冷備文件每天會(huì)定時(shí)上傳到HDFS,提供給業(yè)務(wù)下載和在主備皆故障的情況下做數(shù)據(jù)恢復(fù)使用。
圖三 整體架構(gòu)
四、自動(dòng)化接入
當(dāng)面對(duì)成百上千乃至上萬個(gè)Redis實(shí)例時(shí),人工根據(jù)業(yè)務(wù)申請(qǐng)單去過濾無效節(jié)點(diǎn)、篩選符合業(yè)務(wù)要求的節(jié)點(diǎn)、再從候選節(jié)點(diǎn)中找出最優(yōu)節(jié)點(diǎn)等執(zhí)行一些列繁瑣枯燥流程,這不僅會(huì)導(dǎo)致工作乏味、效率低,而且更會(huì)大大提升系統(tǒng)的不穩(wěn)定性,引發(fā)運(yùn)營事故。當(dāng)繁瑣、復(fù)雜的流程變成自動(dòng)化后,工作就會(huì)變得充滿樂趣,圖三是業(yè)務(wù)接入調(diào)度流程,用戶在運(yùn)維管理系統(tǒng)提單接入后,調(diào)度器會(huì)定時(shí)從CDB中讀取待調(diào)度的業(yè)務(wù)申請(qǐng)單,首先是篩選過濾流程,此流程包含一系列模塊,在設(shè)計(jì)上是可以動(dòng)態(tài)擴(kuò)展,目前實(shí)現(xiàn)的篩選模塊如下:
Health: 健康探測(cè)模塊,過濾宕機(jī)、裁測(cè)下線的節(jié)點(diǎn)IP
Lable:標(biāo)簽?zāi)K,根據(jù)業(yè)務(wù)申請(qǐng)單匹配部署環(huán)境(測(cè)試、現(xiàn)網(wǎng))、部署城市、業(yè)務(wù)模塊、Redis存儲(chǔ)類型(單機(jī)版、分布式存儲(chǔ)版)
Instance: 檢查當(dāng)前節(jié)點(diǎn)上是否有空余的Redis實(shí)例(篩選Redis實(shí)例時(shí))
Capacity: 檢查當(dāng)前節(jié)點(diǎn)CPU、Memory是否超過安全閥值
Role:檢查當(dāng)前節(jié)點(diǎn)角色是否滿足要求(如Redis實(shí)例所屬節(jié)點(diǎn)機(jī)器必須是Redis Node),角色分為三類Proxy Node,Redis Node,Dashboard Node
以上篩選模塊,適用Proxy、Redis、Dashboard節(jié)點(diǎn)的篩選,在完成以上篩選模塊后,返回的是符合要求的候選節(jié)點(diǎn),對(duì)候選節(jié)點(diǎn)我們又需要對(duì)其評(píng)分,從中評(píng)出最優(yōu)節(jié)點(diǎn),目前實(shí)現(xiàn)的評(píng)分模塊有最小內(nèi)存調(diào)度、最大內(nèi)存調(diào)度、最小CPU調(diào)度、隨機(jī)調(diào)度等。
圖四 業(yè)務(wù)接入調(diào)度流程
通過運(yùn)行以上一系列篩選和評(píng)分模塊后,就可以準(zhǔn)確、快速的獲取到新集群的Dashboard、Proxy、Redis的部署節(jié)點(diǎn)地址,但是離自動(dòng)化交付給業(yè)務(wù)使用還差一個(gè)重要環(huán)節(jié)(部署)。目前主要是通過以下三個(gè)方面來解決自動(dòng)化部署,其一,Codis本身是基于配置文件部署的,每新增一個(gè)業(yè)務(wù)集群必須在配置文件指定集群名字,新建一個(gè)PKG包,維護(hù)成本非常高,我們通過監(jiān)聽指定網(wǎng)卡+核心配置項(xiàng)遷移到ZooKeeper,實(shí)現(xiàn)配置管理API化,同時(shí)部署包標(biāo)準(zhǔn)統(tǒng)一化。其二,在各節(jié)點(diǎn)上都會(huì)部署Agent,Agent會(huì)定時(shí)采集上報(bào)各節(jié)點(diǎn)信息入庫到容量表,無需人工干預(yù),容量管理自動(dòng)化,未使用的實(shí)例形成一個(gè)小型資源buffer池。其三,部署是個(gè)多階段的流程,需要分解成各狀態(tài),并保證每個(gè)狀態(tài)都是可重入、冪等性的,當(dāng)所有狀態(tài)完成后,則調(diào)度結(jié)束,某狀態(tài)失敗時(shí),下次調(diào)度檢查到申請(qǐng)單非完成狀態(tài),會(huì)自動(dòng)重試失敗的流程,直至完成,拆分后的部署狀態(tài)流程圖如圖四所示。
通過以上兩個(gè)核心流程,自動(dòng)化調(diào)度分配實(shí)例+自動(dòng)化部署,我們可以將部署時(shí)間從最開始的15min+,優(yōu)化到秒級(jí),在大大提升工作效率的同時(shí),提升了系統(tǒng)穩(wěn)定性、避免了人為操作錯(cuò)誤引起的運(yùn)營事故。
圖五 自動(dòng)化部署流程
五、數(shù)據(jù)遷移
擴(kuò)縮容是存儲(chǔ)系統(tǒng)的常歸化操作,理想中的數(shù)據(jù)遷移應(yīng)該是盡量不影響線上業(yè)務(wù)正常讀寫訪問、支持任意大小的Key、優(yōu)異的遷移性能、保證遷移前后的數(shù)據(jù)一致性,但是Codis在2016年末的時(shí)候數(shù)據(jù)遷移功能差強(qiáng)人意。首先是遷移速度慢,其次是只支持同步遷移,較大的Key遷移會(huì)阻塞Redis主線程,影響線上業(yè)務(wù)正常讀寫,最后是不支持超大Key遷移(>512M)。雖然各種最佳實(shí)踐不斷強(qiáng)調(diào)需要避免大Key,的確大Key可能會(huì)是系統(tǒng)潛在的一個(gè)風(fēng)險(xiǎn)點(diǎn)(如大key刪除、遷移、熱點(diǎn)訪問等),但是在不少業(yè)務(wù)場景下,業(yè)務(wù)層是無法高效、簡單的完成分Key的,Redis本身也在不斷的優(yōu)化,降低大Key風(fēng)險(xiǎn),比如4.0版本提供了異步刪除Key功能,倘若存儲(chǔ)層能快速完成大Key遷移,這不僅會(huì)大大簡化業(yè)務(wù)端的復(fù)雜度,更會(huì)提升Redis穩(wěn)定性、可用性,但是內(nèi)存型存儲(chǔ)系統(tǒng)在大Key遷移的上復(fù)雜度比非內(nèi)存型存儲(chǔ)系統(tǒng)多一個(gè)數(shù)量級(jí),這也是為什么Redis到現(xiàn)在還未實(shí)現(xiàn)大Key遷移和異步遷移的功能。
大Key若能拆分成小Key分批次異步遷移、并在遷移過程中該Key可讀、不可寫,只要遷移速度夠快,這對(duì)業(yè)務(wù)而言是可以接受的,在2016年末的時(shí)候我跟Codis核心作者spinlock交流了大key遷移的想法,令人驚喜膜拜的是,他在農(nóng)歷春節(jié)期間就快馬加鞭實(shí)現(xiàn)了異步遷移原型,在這過程中我們協(xié)助其測(cè)試、反饋BUG和瓶頸、不斷改進(jìn)、優(yōu)化遷移性能,最終異步遷移不僅支持任意大小Key遷移,而且遷移性能相比同步遷移要快5-6倍,我們也是第一個(gè)在線上大規(guī)模應(yīng)用實(shí)踐Redis異步遷移的,更令人可喜的是此異步遷移方案擊敗了Redis作者antirez之前計(jì)劃的多線程方案,將正式合入Redis 4.2版本。
在介紹異步遷移方案實(shí)現(xiàn)前,先介紹下Codis是如何保證過程中數(shù)據(jù)一致性和為什么同步遷移慢。如何保證遷移過程中各Proxy讀取到的數(shù)據(jù)一致性?Codis主要遷移流程如圖五所示,其采用了多階段狀態(tài)機(jī)實(shí)現(xiàn),類似分布式事務(wù)中的多階段提交協(xié)議,其核心流程如下。
在運(yùn)維管理系統(tǒng)上,提交遷移指令,Dashboard更新ZooKeeper上哈希槽狀態(tài)為待遷移,即返回(時(shí)序圖1,2,3步驟)。
Dashboard異步定時(shí)檢查ZooKeeper上是否有待遷移狀態(tài)的哈希槽,若有則首先進(jìn)入準(zhǔn)備中狀態(tài),Dashboard將此狀態(tài)同時(shí)分發(fā)到所有Proxy,若有異常Proxy應(yīng)答失敗,則無法進(jìn)入遷移,狀態(tài)回退(時(shí)序圖4,5,6,7步驟)。
若所有Proxy應(yīng)答成功,則進(jìn)入準(zhǔn)備就緒狀態(tài),Dashboard將此狀態(tài)同時(shí)分發(fā)到所有Proxy,Proxy收到此狀態(tài)后,訪問此哈希槽中的Key的業(yè)務(wù)請(qǐng)求將被阻塞等待,若有Proxy應(yīng)答失敗,則會(huì)立刻回退到上個(gè)狀態(tài)(時(shí)序圖8,9,10步驟)。
若所有Proxy應(yīng)答成功,則進(jìn)入遷移狀態(tài),Dashboard將此狀態(tài)同時(shí)分發(fā)到所有Proxy,Proxy收到此狀態(tài)后,不再阻塞對(duì)遷移哈希槽中的Key訪問,若業(yè)務(wù)請(qǐng)求Key屬于待遷移哈希,首先會(huì)從遷移源Redis中讀取數(shù)據(jù),寫到目的端Redis中去,然后再獲取/修改數(shù)據(jù)返回,這是其中一種遷移方式,被動(dòng)遷移,Dashboard也會(huì)發(fā)起主動(dòng)遷移,直至數(shù)據(jù)遷移結(jié)束(時(shí)序圖11,12,13,14,15步驟)。
通過多階段的狀態(tài)提交和細(xì)粒度、ms級(jí)別的鎖,Codis優(yōu)雅的解決了遷移過程中的數(shù)據(jù)一致性。
圖六 Codis遷移狀態(tài)流程圖
圖七 Redis同步遷移流程
再看為什么同步遷移慢,圖七是遷移一個(gè)1000萬元素的ZSET耗時(shí)分析,當(dāng)Client發(fā)起遷移指令后,源端將整個(gè)ZSET序列化成payload花費(fèi)了10.27s,通過網(wǎng)絡(luò)傳輸給目的端Redis花費(fèi)1.65s,目的端Redis收到數(shù)據(jù)后,將其反序列化成內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),花費(fèi)了36.65s,最后源端Redis刪除遷移完成的Key又花費(fèi)了6.11s,而整個(gè)遷移過程中,源端Redis是完全阻塞的,不能提供任何讀寫訪問。因此,異步遷移方案若要提升遷移性能,必須在以上四個(gè)流程上面做優(yōu)化。
異步遷移的流程如圖八所示,面對(duì)同步遷移的四個(gè)核心點(diǎn),異步遷移的解決方案如下:
-
拆分rdbSave(encoding)過程,解決同步序列化開銷。對(duì)于大key,不再使用rdbSave對(duì)數(shù)據(jù)進(jìn)行encoding,而是通過指令拆解, redis中的數(shù)據(jù)結(jié)構(gòu)(list,set,hash,zset)都可以等價(jià)的拆分成若干個(gè)添加指令,比如含有1000萬元素的zset,可以拆分成10萬個(gè)zadd指令,每個(gè)zadd指令添加100個(gè)數(shù)據(jù)
-
拆分Restore(network)過程,解決同步IO開銷。異步IO實(shí)現(xiàn),發(fā)送數(shù)據(jù)不再阻塞。
-
拆分rdbLoad(decoding)過程,解決反序列號(hào)開銷。因源端發(fā)送過來的數(shù)據(jù)不再是rdb二進(jìn)制數(shù)據(jù),目的端redis無需再使用rdbLoad,只需將收到的添加指令數(shù)據(jù)直接更新到對(duì)應(yīng)的內(nèi)存數(shù)據(jù)結(jié)構(gòu)即可,同時(shí)使用了一些trick,比如內(nèi)存預(yù)分配,避免頻繁申請(qǐng)內(nèi)存,double轉(zhuǎn)換成long long,提高遷移性能等。
-
異步刪除Key,解決同步刪除Key耗時(shí)問題。通過額外的工作線程異步刪除key,不再阻塞redis主線程。
圖八 Redis異步遷移流程
1000萬的ZSET,同步遷移需要54.87s,而異步遷移只需要8.3s,在不阻塞在線業(yè)務(wù)的前提下,性能提升6倍多,以我們生產(chǎn)環(huán)境某全球9000w排行榜為例,之前單機(jī)主備版加載到內(nèi)存都需要20分鐘,而用異步跨機(jī)器遷移只需要180s左右, 更詳細(xì)的遷移介紹可參看附錄spinlock的Codis新版本特性介紹。
六、高可用
各組件中跟用戶請(qǐng)求相關(guān)性很強(qiáng)的組件分別是Proxy、Redis、元數(shù)據(jù)存儲(chǔ)(ZooKeeper),相關(guān)性較弱的是Dashboard。
Proxy:多機(jī)多IDC部署,調(diào)度服務(wù)會(huì)根據(jù)IDC ID,自動(dòng)打散相同proxy,盡量保證同一集群proxy部署在不同IDC,通過L5和CMLB進(jìn)行容災(zāi)。
Redis:基于Redis Sentinel進(jìn)行主備自動(dòng)化切換。
ZooKeeper:高可用分布式協(xié)調(diào)服務(wù),一半以上節(jié)點(diǎn)存活即可提供服務(wù),同時(shí)只有在Proxy啟動(dòng)時(shí)和運(yùn)行過程中發(fā)生數(shù)據(jù)遷移才會(huì)依賴ZooKeeper,絕大部分正常請(qǐng)求不受ZooKeeper 集群狀態(tài)影響。
Dashboard: 負(fù)責(zé)協(xié)調(diào)集群狀態(tài)變更及一致性,目前在設(shè)計(jì)上是個(gè)單點(diǎn),但是只有在就集群運(yùn)行過程中發(fā)生數(shù)據(jù)遷移才會(huì)依賴它,因此是弱相關(guān)性, 后續(xù)還可以優(yōu)化成多節(jié)點(diǎn)部署,通過ZooKeeper的分布式鎖來保證只有一個(gè)節(jié)點(diǎn)能提供服務(wù),當(dāng)提供服務(wù)的節(jié)點(diǎn)故障時(shí),通過一系列流程(如需通知Proxy,Dashboard變更等)實(shí)現(xiàn)Dashboard自動(dòng)化故障切換。
重點(diǎn)介紹redis的主備自動(dòng)切換流程,常見的Master-Slave存儲(chǔ)系統(tǒng)自動(dòng)切換方案一般有如下三種:
-
基于ZooKeeper來做主備自動(dòng)切換,如公司內(nèi)部的TDSQL,在Mysql主備節(jié)點(diǎn)上部署Agent,在ZooKeeper集群上注冊(cè)臨時(shí)節(jié)點(diǎn),當(dāng)主機(jī)宕機(jī)時(shí),Scheduler在檢測(cè)到臨時(shí)節(jié)點(diǎn)消失超過閥值后發(fā)起容災(zāi)流程。
-
基于相互獨(dú)立的探測(cè)Agent實(shí)現(xiàn),如MIG的DCache,IEG的TRedis。IEG TRedis將主備自動(dòng)切換流程拆分成故障決策模塊(探測(cè)Redis存活)、故障同步模塊、故障監(jiān)控模塊(double check)、故障切換表同步模塊(將待切換的實(shí)例放入隊(duì)列)、故障核心模塊( 切換路由)。
-
基于Quorum的分布式探測(cè)Agent,如Redis的Sentinel,Sentinel在新浪微博等公司已經(jīng)進(jìn)行了較大規(guī)模應(yīng)用,Codis也是基于此實(shí)現(xiàn)主備自動(dòng)切換,我們?cè)诖嘶A(chǔ)上增加了告警和當(dāng)網(wǎng)絡(luò)出現(xiàn)分區(qū)時(shí),增加了一個(gè)降級(jí)操作,避免腦裂,其詳細(xì)流程圖八所示。
圖九 Redis主備自動(dòng)切換流程
圖九流程簡要分析如下:
圖中三個(gè)Sentinel部署在不同的可用區(qū),實(shí)際現(xiàn)網(wǎng)我們是部署了五個(gè),覆蓋各大運(yùn)營商IDC等,各Sentinel會(huì)定時(shí)向master/slave發(fā)送ping等請(qǐng)求探測(cè)master/slave存活,并通過gossip協(xié)議相互交流信息,同時(shí)各Proxy實(shí)時(shí)監(jiān)聽Sentinel的狀態(tài)消息。(圖八1,2流程)
當(dāng)master出現(xiàn)異常,Sentinel在一定時(shí)間(可配置,如2min,避免網(wǎng)絡(luò)抖動(dòng),誤切)內(nèi)都持續(xù)無法訪問Master時(shí),Sentinel就會(huì)認(rèn)為此節(jié)點(diǎn)為主觀故障(S_DOWN),Sentinel會(huì)彼此通過gossip協(xié)議相互交換信息,當(dāng)一半以上(可配置)的sentinel認(rèn)為此master都故障后,此節(jié)點(diǎn)會(huì)被判斷為客觀故障(O_DOWN),各Sentinel會(huì)選舉出一個(gè)Leader來執(zhí)行主備切換,Leader首先從各備機(jī)中選擇一個(gè)最佳節(jié)點(diǎn),算法是首選過濾掉與master斷線時(shí)間超過閥值的slave,其次優(yōu)先選擇slave_priority較小的,若priority一樣,則選擇replication offset最大的,若offset也一致,則按字典順序排序選擇最小的runid.選擇出最佳候選master后,Leader會(huì)將其提升為master,同時(shí)向訂閱者發(fā)出+switch-master 事件,通過tnm2/uwork發(fā)出L0告警通知開發(fā)運(yùn)維,然后更改其他備機(jī)主從關(guān)系,從新主機(jī)同步數(shù)據(jù)(圖八3,4,5流程)。
各個(gè)Proxy收到Sentinel的+switch-master event后,會(huì)遍歷所有sentinel查詢故障組最新master,當(dāng)一半以上的sentinel返回了故障組新的master,Proxy則會(huì)切換路由,路由到組的請(qǐng)求,將發(fā)到新的master,主備自動(dòng)切換完成。(圖八6流程)
但是在極端情況下若網(wǎng)絡(luò)出現(xiàn)分區(qū),業(yè)務(wù)服務(wù)、個(gè)別Proxy跟Redis Master在同一個(gè)可用區(qū),則會(huì)出現(xiàn)腦裂,為了避免此種情況,部署在Redis機(jī)器上的Agent會(huì)定時(shí)持續(xù)檢測(cè)與ZooKeeper連接是否通暢,若連接不上則會(huì)向Redis發(fā)送降級(jí)指令,不可讀寫(圖八7流程)。
七、運(yùn)營實(shí)踐
1.多維度監(jiān)控
Proxy/Dashboard/Redis機(jī)器上的Agent定時(shí)采集proxy、redis的qps、connection、memory_used等10幾個(gè)指標(biāo),上報(bào)到米格監(jiān)控系統(tǒng),針對(duì)核心監(jiān)控指標(biāo)配置閥值和波動(dòng)告警。監(jiān)控系統(tǒng)在線上數(shù)次捕捉到集群異常(如連接數(shù)超過閥值、某redis實(shí)例無備機(jī)等),及時(shí)發(fā)出有效告警,提前發(fā)現(xiàn)問題、解決問題。同時(shí),也不連VPN的情況下也可以便捷地通過手機(jī)快速查看監(jiān)控曲線、定位問題等,大大提高工作效率。
圖十 Redis Ops曲線
圖十一 master/slave offset差異曲線
2 . 低負(fù)載優(yōu)化
集群縮容和相同業(yè)務(wù)復(fù)用同集群
存儲(chǔ)機(jī)多實(shí)例部署,現(xiàn)在默認(rèn)8個(gè)實(shí)例
通過Agent順序觸發(fā)個(gè)實(shí)例aof rewrite和rdb save,避免多個(gè)實(shí)例同時(shí)fork,從而提高存儲(chǔ)機(jī)內(nèi)存使用率至最高80%
Proxy機(jī)器多實(shí)例部署(進(jìn)行中)
3 .多租戶
小業(yè)務(wù)通過在key前綴增加業(yè)務(wù)標(biāo)識(shí),復(fù)用相同集群
大業(yè)務(wù)使用獨(dú)立集群,獨(dú)立機(jī)器
4.數(shù)據(jù)安全及備份
訪問所有Redis實(shí)例都需要鑒權(quán)
Proxy層可統(tǒng)計(jì)匯總所有寫請(qǐng)求指令
默認(rèn)開啟AOF日志
定時(shí)上報(bào)Redis AOF、RDB文件到HDFS集群
八、總結(jié)
基于Codis為核心的Redis服務(wù)平臺(tái)高效解決了SNG大量業(yè)務(wù)的痛點(diǎn)(不限制Key大小,原生的Redis內(nèi)核,高性能),提高了開發(fā)效率,助力產(chǎn)品更快發(fā)展,但是因人力有限(半個(gè)開發(fā)投入,在業(yè)務(wù)項(xiàng)目人力緊張的時(shí)候,零投入),還有若干待完善的地方,如不支持冷熱分離等。 在千呼萬喚中,目前公司內(nèi)的存儲(chǔ)組自研的CKV+(基于共享內(nèi)存實(shí)現(xiàn)Redis各類數(shù)據(jù)結(jié)構(gòu))的單機(jī)主從版也終于上線,集群版也在緊鑼密鼓的開發(fā)中,CKV+較好的解決了Redis內(nèi)存使用率、跨IDC部署、數(shù)據(jù)備份及同步機(jī)制的一些不足之處,后續(xù)業(yè)務(wù)也將有更多的選擇!最后感謝antirez,spinlock的無私貢獻(xiàn)!
九、參考資料
Redis Documentation
Github Codis
Github Redis
Codis新版本特性介紹(spinlock)
核心關(guān)注:拓步ERP系統(tǒng)平臺(tái)是覆蓋了眾多的業(yè)務(wù)領(lǐng)域、行業(yè)應(yīng)用,蘊(yùn)涵了豐富的ERP管理思想,集成了ERP軟件業(yè)務(wù)管理理念,功能涉及供應(yīng)鏈、成本、制造、CRM、HR等眾多業(yè)務(wù)領(lǐng)域的管理,全面涵蓋了企業(yè)關(guān)注ERP管理系統(tǒng)的核心領(lǐng)域,是眾多中小企業(yè)信息化建設(shè)首選的ERP管理軟件信賴品牌。
轉(zhuǎn)載請(qǐng)注明出處:拓步ERP資訊網(wǎng)http://www.oesoe.com/
本文標(biāo)題:大規(guī)模 codis 集群的治理與實(shí)踐
本文網(wǎng)址:http://www.oesoe.com/html/solutions/14019321459.html