折800緩存服務的構建

折800緩存服務的構建

1. 背景

zhe800公司內大量使用redis,且用途多樣:緩存、隊列、數據庫,都有功能。同時公司內對redis的使用比較隨意,每個應用按自己的需求要求運維部署redis-server,導致對redis的使用很混亂,運維部門都無法精確得知redis-server的部署情況。經過長時間的調查、搜集資料才整理出公司內redis的使用情況,目前部署數百多個redis-server實例、版本分為:2.8.9、2.8.11、2.8.17、2.8.19、2.8.23 多種。

2. 需求的演變

鑒于公司redis使用、管理混亂的情況,公司要求改善對redis的使用和管理,由成都“基礎架構部”負責此項改進。此時產生了第一階段需求:

1、通過一個統一管理系統管理公司內所有的redis進程

2、可以通過這個管理系統監控redis的運行狀態

3、redis資源通過管理系統來分配

針對第一階段的需求,我們基礎架構部和用戶、運維部、技術委員會進行了多次討論,認為目前redis使用的問題:

沒有實現跨機房高可用,這點很重要,公司內所有應用要求必須跨機房高可用。

沒有考慮橫向擴展問題。

一部分隊列、持久化等功能不適合使用redis,應該使用更“專業”的隊列系統和數據庫解決。

此時對第一階段需求進行了修改,產生了第二階段需求:

1、redis僅作為緩存使用。

2、實現橫向擴展。

3、實現跨機房高可用,雙機房至少要實現“溫備”。

4、由一個后臺系統管理、分配、監控redis資源。

其中第3個需求:實現跨機房高可用最為困難,因為緩存的典型場景:緩沖數據庫查詢,如果主機房失效后,切換到備機房的緩存服務,此時備機房緩存沒有數據(是”冷“的),會導致對數據庫(MySQL)的查詢量爆增;因此要求備機房的緩存必須至少有主機房的部分數據,在主機房失效的情況下才能防止數據庫查詢量爆增,而且在緩存的持續使用下,備機房緩存會很快達到主機房的狀態。

因此跨機房數據同步是必須的。

3. 選型

在最終需求的指導下,已經明確我們基礎架構部需要打造一套緩存服務系統,包含redis-server和管理、監控系統。

此時我們有兩種方案可選擇:

1. 基于代理的橫向擴展方案

以codis為代表的基于代理的集群方案,通過在redis-client和redis-server之間增加一個代理層,通過代理層sharding鏈接到正確的redis-server進行操作。

2. 基于redis官方集群的方案

redis從3.0版開始提供了很受期待的集群功能,按照CAP理論,redis cluster屬于保證AP,放棄C,僅能提供最終一致性。按照redis cluster的協議,client需要在本地sharding,選擇正確的redis結點,然后進行操作,否則會返回MOVE錯誤。

調研結論是:采用redis官方集群方案

有以下原因促使我們選擇redis cluster方案:

1、基于代理的方案會導致性能下降,對于一個緩存服務來說性能很重要。

2、redis cluster方案較簡單,不需要部署額外的進程,其本身就能實現高可用了,如果是基于代理的方案,代理本身也需要高可用,這增加了復雜度。

3、redis cluster是官方支持的自帶功能,比起第三方開發的代理進程,可能更穩定可靠。

4、redis cluster能實現讀擴展、讀寫分離功能;如果使用代理,那么讀寫擴展時必須同時考慮redis-server和代理程序。

redis cluster仍然不能實現跨機房容災,跨機房高可用的功能最后決定由基礎架構部自行實現

4. redis cluster簡介

其基本思想是將數據放置于槽(slots)中,slot有0x3fff(16384)個。為了實現數據分片,這些槽分布在多個redis master結點中,為了實現高可用,每個master結點擁有零到多個slave結點,這些slave同步保存master的數據,一旦master crash以后,會選舉他的slave結點成為新的master。

僅master結點可寫入,然后數據會異步同步到slave,slave可讀不可寫。

因此理想情況下:結點永不crash、只讀寫master結點,redis cluster可保證強一致。 但因為現實使用中,結點可能crash,且可讀slave結點,因此redis cluster僅能保證最終一致性。

redis cluster通過謠言傳播同步結點的狀態,每一個結點都保存了所有結點的狀態信息,其中最重要的是:結點的ip+端口、每個節點包含的slots。

client 集群模式流程:

1、從任意一個集群的結點獲取所有結點的狀態(使用CLUSTER NODES命令),同時獲知了每一個slot所在的結點。結點有兩個角色:master/slave。

2、在get/set/hget/hset等命令時,對key進行計算:crc16(key) & 0x3fff,所得的結果就是slot編號。

3、通過slot編號獲取結點的ip+port,然后發送此結點。

4、此時如果返回-ASK ip:port錯誤,表明slot臨時發生了變化,此時應往ASK指定的結點先發送ASKING命令,再發送實際的命令。

5、此時如果返回-MOVED slot-number ip:port錯誤,表明集群發生了變化(擴容、縮減、槽遷移過),則先將命令發送到MOVED錯誤指明的結點后,重新獲取集群結點狀態,以同步最新的結點狀態。

5. 管理系統的設計與實現

zhe800基于redis cluster的緩存服務被命令為z(he800-r)edis,包含一個管理系統對所有緩存集群進行管理,以及雙機房數據同步機制,保證主備兩個機房之間部分數據同步,實現雙機房容災。

對于zedis,我們有幾個指導原則:

1、簡單:復雜的方案容易出錯,且成本高,因此在簡單和復雜之中選擇簡單的方案。

2、用戶透明:不要要求用戶了解大量的細節才能使用。

3、可用:可正常使用,可從錯誤中恢復。

5.1 管理系統的功能設計

緩存集群的操作:

容量擴容/讀寫擴容:增加1個到多個分片,同時擴展了讀寫能力。

高可用/讀擴容:僅增加slave結點數,增強高可用性,同時擴展了讀能力。

1、分配:按應用的需求分配一個全新的redis cluster,如果配置了跨機房容災選項,還會按需求在備機房啟動一個災備集群。

2、下線:將一個運行中的redis cluster刪除,并進行清理。

3、擴容:

4、縮減:擴容的逆操作,支持讀寫縮減和讀縮減。

監控:

1、物理機資源監控:監控物理機當前的資源,為緩存集群的操作提供依據。

2、集群監控:對每個節點到集群本身進行監控,提供給運維查看。

3、報警:對接公司報警系統,遇到結點crash、集群不可用等情況及時發出報警

緩存集群的操作需要以物理機資源數據為依據,這部分數據通過zabbix獲取。同時集群的每個節點需要使用cgroup進行資源隔離,這些操作都由管理后臺自動進行。

對物理機的操作:啟動redis-server進程、創建執行文件夾、生成redis.conf文件等操作通過salt進行,因為公司內每臺服務器都安裝了salt-minion,因此可直接利用。

5.2 事務支持

對redis cluster的各種操作都是通過redis client向redis-server發送命令實現,但是這些操作必須是原子操作,長時間的操作必須可中途放棄并會滾到初始狀態,否則對redis cluster的操作風險會比較高,運維人員可能不敢使用管理系統。

事務系統采取模仿數據庫的一種實現方式:記錄日志。數據庫日志分為:undo、redo、undo/redo三種形式。

我們采取undo日志的形式,對于管理系統四大操作:

分配

下線

擴容

縮減

都通過事務系統執行。

模仿數據庫,事務管理器提供三個原語:

1、begin: 開始一個事務,并返回一個事務id(tid)

2、abort-rollback: 中途放棄,會滾到begin時候的狀態

3、commit: 提交事務,標記事務已經完成。

四大操作每一個步驟都會在操作成功后,記錄undo日志到文件系統中,因此還需要確保每個“步驟”是原子操作,如果遇到abort或者錯誤時,逆向執行undo日志中命令就能恢復到初始狀態了。如果rollback仍然失敗,則說明遇到了管理系統無法處理的故障(如機器斷電、網絡中斷等)此時需要暫停會滾,通知運維人員恢復故障,再進行回滾。

6. 高可用的實現

6.1 redis cluster的高可用

redis很早就可以通過master-slave機制實現雙機熱備,到了cluster時代,仍然保留了master-slave機制。

在cluster模式中,每個master結點保存了一部分slots,同時每個master結點可設置0~多個slave結點,master的數據會異步同步到slave結點(redis的特點:所有IO操作皆異步),這樣一個master-slave的組合可稱之為一個partition;一個partition是高可用的:

slave結點crash,對集群本身無影響,對partition無影響;slave結點重新連接上master后會檢查自己與master的數據差別,如果差距太大,會先進行一次全量同步,然后開始增量同步。

master結點crash,對集群本身無影響,對partition有影響;集群會選舉一個slave作為新的master,因為slave結點幾乎有master結點的所有數據,因此數據僅有小概率丟失。

partition整體crash,集群會整體不可用,因為集群認為slots不連續了,保存在這個partition中的slots無法訪問。如果沒開啟持久化,會導致這部分數據永久丟失。

可見,redis cluster做到了一定的高可用,我們使用jedis測試結果來看,在一個1主1備的redis cluster中,隨機kill一個結點不會導致任何錯誤,但kill一個master結點和它所有的slave結點就會導致集群不可用的錯誤,此時無法get/set數據。

6.2 對于高可用的思考

對于redis cluster高可用的研究和實驗發現,其應對單機房高可用是可以勝任的,已有的客戶端:jedis新版本也能很好的處理高可用問題。

但對于跨機房高可用/容災,仍然沒有發現現成的方案可供使用,對此有以下方案被提出:

1、客戶端雙寫:客戶端需要連接到主、備兩個集群,同步寫主、異步寫備;平時讀主集群的數據,主集群不可用后馬上切換為同步寫備、讀備。

2、集群間數據同步:開啟持久化,然后同步redis的數據庫文件。

3、服務端雙寫:redis-server將set/hset等寫命令再寫一份到備集群。

最終我們選擇了第三個方案:服務端雙寫。原因為:

按照用戶透明原則,如果要求客戶端雙寫,客戶端需要做的事太多,而服務端雙寫方案客戶端僅需處理主備集群切換就可以了。

集群間數據同步方案,要求數據先寫到文件中,在同步到災備集群,這其實是不必要的,因為如果做到雙機房容災,持久化都是可以關閉的,因為partition整體crash的集群大幅度下降了(按平方下降,如果單個partition crash的幾率為p,主備parttion同時crash的幾率僅有p^2,且p隨著slave結點的數量指數級的變小),退一步說:因為本身為緩存服務,可接受少量數據丟失。

服務端雙寫足夠簡單,僅需要將客戶單對redis-server的寫入命令復制一份發送到災備集群即可。

服務端雙寫并不需要完全雙寫,僅保證災備集群擁有主集群的部分數據即可,因為對于緩存服務來說,只要發生災備時,災備集群不“冷”就可以接受了;同時這也節省了機房間光纖帶寬。

6.3 跨機房高可用

跨機房高可用的基本思想為:在主備兩個兩個機房各啟動一個redis cluster,主機房集群在寫入數據的時候,同時寫入一分到備機房。

要達到以上設計目的,必須對官方redis進行修改,我們選擇了版本3.2.0為基礎進行修改。 redis的一個重要設計思想是所有IO操作皆異步非阻塞進行,為此redis封裝了ae一個事件處理庫封裝了各個操作系統提供的事件處理模型:

OS X/Darwin/BSD:kqueue

Linux:epoll

Sun OS:evport

Others:select

在3大操作系統中并不支持Windows的IO Complete Port,原因是IOCP是真異步模型,在收到事件通知時,數據已經接收到/發送完畢,而不像epoll,kqueue等模型,在收到事件通知時,還需要再調用read/write,通知僅僅是告訴用戶態可讀/可寫了,而遠不同于IOCP,通知是通知用戶態讀/寫已經完成了。由此可見IOCP現在比較難融入redis現有的IO層中。

我們先開始研究redis的代碼,可得知它的IO層設計:

redis執行一個命令的時序:

最終我們決定在processInputBuffer中,解析redis協議時,將寫操作按配置的百分比過濾后,復制一份發送到備機房;此時有新的考慮:

備機房的接收者也是集群,如果需要發送給集群,那么勢必會增加對redis-server代碼的修改程度,因為還必須處理目標集群的高可用、sharding問題,風險增加。我們希望對redis-server的修改盡可能的小。

如果機房間光纖中斷,會丟失一部分數據;雖然作為緩存集群,數據可以丟失一小部分,但能保證數據不丟失盡量不丟。

只能寫入另一個redis cluster中,不靈活。

因此我們增加了一個名為zedis-gateway的代理程序,redis-server中復制出來的寫操作先發送到zedis-gateway,zedis-gateway負責對這些寫操作進行處理,發送到備份集群。增加zedis-gateway的好處:

zedis-gateway可靈活處理接收到的寫操作,比如:寫入備份集群、寫入MySQL數據庫中、等等。

zedis-gateway負責處理較復雜的,寫入備份集群的failover、sharding問題,簡化了對redis-server的修改。

如果機房間光纖中斷,zedis-gateway可將寫操作暫時寫入文件;等待通信恢復后在從文件中發送暫時保存的寫操作;數據可以不丟失。

首先,我們為redis增加了兩個配置項:

同時zedis-gateway也需要高可用,我們簡單實現:

每個redis-server可配置多個zedis-gateway地址,從中隨機選擇一個連接發送寫命令。

如果某個zedis-gateway地址連接不上,或連接錯誤,則再隨機選一個,直到能連上一個正確的或者一個也不能連上。

如果沒有一個zedis-gateway地址連接成功,則寫入錯誤日志;但不影響其他操作。

最后將官方版redis-server修改為:

雙機房容災zedis集群結構為:

實現結果:

我們實現了主備機房間集群數據0~100%同步,但為了節省機房間光纖帶寬,一般不允許開啟100%同步。

性能損失:在100%雙寫的配置下,性能損失<10%。

動態調整:所有關于雙寫的配置都可以在運行中隨時調整。

可維護:zedis-gateway可以隨時更新版本,而不會影響雙寫。

7. 問題與展望

目前的zedis還存在一些問題,需要我們持續已經,以達到完善。

1、redis client中,只有jedis對集群支持最好,其他語言版本,如ruby、node.js、golang版本的客戶端還需要進行一定改造才能支持redis cluster。

2、zedis-gateway目前還是單進程、單線程模型,如果未來它成為瓶頸,我們還需要增加進程數;后續根據需求也許需要將它改造為類似nginx的多進程模型。

3、修改版的redis還需要優化。

4、目前只能從主到備雙寫數據,如果從主切換到備,再從備切換到主,會丟失一部分數據;因為目前定位為緩存集群,因此可接受這個損失,如果今后需要升級作為內存數據庫使用,我們就還需要處理這個問題。

5、只有部分命令支持雙寫,剩余的還在添加中。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容