同程旅游緩存系統(tǒng)設計:如何打造Redis時代的完美體系(含PPT)
導讀:高可用架構 7 月 30 日在上海舉辦了『互聯(lián)網(wǎng)架構的基石』專題沙龍腹忽,進行了閉門私董會研討及對外開放的四個專題的演講,期望能促進業(yè)界對互聯(lián)網(wǎng)基礎服務及工具的討論砚作,本文是王曉波分享同程旅游緩存系統(tǒng)架構經(jīng)驗窘奏。
王曉波,同程旅游首席架構師葫录,專注于高并發(fā)互聯(lián)網(wǎng)架構設計着裹、分布式電子商務交易平臺設計、大數(shù)據(jù)分析平臺設計米同、高可用性系統(tǒng)設計骇扇,基礎云相關技術研究,對 Docker 等容器有深入的實踐面粮。另對系統(tǒng)運維和信息安全領域也大量的技術實踐少孝。曾設計過多個并發(fā)百萬以上、每分鐘 20 萬以上訂單量的電商交易平臺熬苍,熟悉 B2C稍走、B2B袁翁、B2B2C、O2O 等多種電商形態(tài)系統(tǒng)的技術設計婿脸。熟悉電子商務平臺技術發(fā)展特點粱胜,擁有十多年豐富的技術架構、技術咨詢經(jīng)驗狐树,深刻理解電商系統(tǒng)對技術選擇的重要性焙压。
旅游大家現(xiàn)在比較熟悉,同程旅游涵蓋的業(yè)務比較多抑钟,從火車票到住宿都有涯曲。今天我們講一下同程旅游的緩存系統(tǒng),包括整個緩存架構如何設計味赃。先來看一下緩存我們走過了哪些歷程掀抹。
從 memcache 開始使用緩存
再從 memcache 轉(zhuǎn)到 Redis
從單機 Redis 升級到集群 Redis
從簡單的運維升級到全自動運維
重開發(fā) Redis 客戶端
開發(fā) Redis 調(diào)度治理系統(tǒng)
Redis 部署全面 Docker 化
旅游是比較復雜的業(yè)務,住心俗、吃傲武、玩相關功能都會壓到平臺上,整個在線旅游應用量非常大城榛,酒店揪利、機票,或者是賣一個去泰國旅行團狠持,業(yè)務邏輯完全不一樣疟位,因此眾多業(yè)務會帶來大量系統(tǒng)的訪問壓力。
Redis 遍地開花的現(xiàn)狀及問題
Redis 集群的管理
所有互聯(lián)網(wǎng)的應用里面喘垂,可能訪問最多的就是 cache甜刻。一開始時候一些團隊認為 cache 就像西游記里仙丹一樣,當一個系統(tǒng)訪問過大扛不住時正勒,用一下 Redis得院,系統(tǒng)壓力就解決了。在這種背景下章贞,我們的系統(tǒng)里面 Redis 不斷增多祥绞,逐漸增加到幾百臺服務器,每臺上面還有多個實例鸭限,因此當存在幾千個 Redis 實例后蜕径,可能運維也很難說清楚哪個 Redis 是什么業(yè)務。
單點故障
這種背景下會存在什么樣痛苦的場景败京?正常情況下 cache 是為了增加系統(tǒng)的性能兜喻,是畫龍點睛的一筆,但是當時我們 cache 會是什么樣喧枷?它掛了就可能讓我們整個系統(tǒng)崩潰虹统。比如說 CPU 才 5%弓坞,也許就由于緩存問題系統(tǒng)就掛了。
高可用與主從同步問題
因為 cache 有單點车荔,我們想放兩個不就好了嗎渡冻,所以就做了主從。這時候坑又來了忧便,為什么呢族吻?比如有些 Redis 值非常大,如果偶爾網(wǎng)絡質(zhì)量不太好珠增,就會帶來主從不同步超歌,當兩邊主和從都死或者出問題的時,重啟的時間非常長蒂教。
監(jiān)控
為了高可用巍举,我們需要全面的監(jiān)控。當時我們做了哪些監(jiān)控呢凝垛?
connected_clients :已連接客戶端的數(shù)量
client_longest_output_list :當前連接的客戶端當中懊悯,最長的輸出列表
client_longest_input_buf: 當前連接的客戶端當中,最大輸入緩存
blocked_clients: 正在等待阻塞命令(BLPOP梦皮、BRPOP炭分、BRPOPLPUSH)的客戶端的數(shù)量
used_memory_human: 以人可讀的格式返回 Redis 分配的內(nèi)存總量
used_memory_rss: 從操作系統(tǒng)的角度,返回 Redis 已分配的內(nèi)存總量(俗稱常駐集大薪?稀)捧毛。這個值和 top 、 ps 等命令的輸出一致让网。
replication: 主/從復制信息
instantaneous_ops_per_sec: 服務器每秒鐘執(zhí)行的命令數(shù)量呀忧。
下面是一個接近真實場景運維與開發(fā)的對話場景。
開發(fā):Redis 為啥不能訪問了溃睹?
運維:剛剛服務器內(nèi)存壞了荐虐,服務器自動重啟了
開發(fā):為什么 Redis 延遲這么大?
運維:不要在 Zset 里放幾萬條數(shù)據(jù)丸凭,插入排序會死人啊
開發(fā):寫進去的 key 為什么不見了?
運維:Redis 超過最大大小了啊腕铸,不常用 key 都丟了啊
開發(fā):剛剛為啥讀取全失敗了
運維:網(wǎng)絡臨時中斷了一下惜犀,從機全同步了,在全同步完成之前狠裹,從機的讀取全部失敗
開發(fā):我需要 800G 的 Redis虽界,什么時候能準備好?
運維:線上的服務器最大就 256G涛菠,不支持這么大
開發(fā):Redis 慢得像驢莉御,服務器有問題了撇吞?
運維:千萬級的 KEY,用 keys*礁叔,慢是一定了牍颈。
因此我們一個架構師最后做了以下總結
從來沒想過,一個小小的 Redis 還有這么多新奇的功能琅关。就像在手上有錘子的時候煮岁,看什么都是釘子。漸漸的涣易,開發(fā)規(guī)范倒是淡忘了画机,新奇的功能卻接連不斷的出現(xiàn)了,基于 Redis 的分布式鎖新症、日志系統(tǒng)步氏、消息隊列、數(shù)據(jù)清洗等徒爹,各種各樣的功能不斷上線荚醒,從而引發(fā)各種各樣的問題。運維天天疲于奔命瀑焦,到處處理著 Redis 堵塞腌且、網(wǎng)卡打爆、連接數(shù)爆表……
總結了一下榛瓮,我們之前的緩存存在哪些問題铺董?
使用的者的亂用、爛用禀晓、懶用精续。
運維一個幾百臺毫無規(guī)則的服務器
運維不懂開發(fā),開發(fā)不懂運維
緩存在無設計無控制中被使用
開發(fā)人員能力各不相同
使用太多的服務器
懶人心理(應對變化不夠快)
我們需要一個什么樣的完美緩存系統(tǒng)粹懒?
我相信上面這些情況在很多大量使用 Redis 的團隊中都存在重付,如果發(fā)展到這樣一個階段后,我們到底需要一個什么樣的緩存凫乖?
服務規(guī)模:支持大量的緩存訪問确垫,應用對緩存大少需求就像貪吃蛇一般
集群可管理性:一堆孤島般的單機服務器緩存服務運維是個迷宮
冷熱區(qū)分:現(xiàn)在緩存中的數(shù)據(jù)許多并不是永遠的熱數(shù)據(jù)
訪問的規(guī)范及可控:還有許多的開發(fā)人員對緩存技術了解有限,胡亂用的情況很多
在線擴縮容:起初估算的不足到用時發(fā)現(xiàn)瓶頸了
這個情況下帽芽,我們?nèi)タ紤]使用更好的方案删掀,本來我們是想直接使用某個開源方案就解決了,但是我們發(fā)現(xiàn)每個開源方案針對性的解決 Redis 上述痛點的某一些問題导街,每一個方案在評估階段跟我們需求都沒有 100% 匹配披泪。每個開源方案本身都很優(yōu)秀,也許只是說我們的場景的特殊性搬瑰,沒有任何否定的意思款票。
下面我們當時評估的幾個開源方案控硼,看一下為什么當時沒有引入。
CacheCloud:跟我們需要的很像艾少,它也做了很多的東西卡乾,但是它對我們不滿足是部署方案不夠靈活,對運維的策略少了點姆钉。
Codis:這個其實很好说订,當年我們已經(jīng)搭好了準備去用了,后來又下了潮瓶,因為之前有一個業(yè)務需要 800G 內(nèi)存陶冷,后來我們發(fā)現(xiàn)這個大集群有一個問題,因為用得不是很規(guī)范毯辅,如果在這種情況下給他一個更大的集群埂伦,那我們可能死的機率更大,所以我們也放棄了思恐。另外 800G 也很浪費沾谜,并不完全都是熱數(shù)據(jù),我們想把它存到硬盤上一部分胀莹,很多業(yè)務使用方的心理是覺得在磁盤上可能會有性能問題基跑,還是放在 Redis 放心一點,其實這種情況基本不會出現(xiàn)描焰,因此我們需要一定的冷熱區(qū)分支持媳否。
Pika:Pika 可以解決上面的大量數(shù)據(jù)保存在磁盤的問題,但是它的部署方案少了點荆秦,而且 Pika 的設計說明上也表示主要針對大的數(shù)據(jù)存儲篱竭。
Twemproxy:最后我們想既然直接方案不能解決,那可以考慮代理治理的方式步绸,但是問題是它只是個代理掺逼,Redis 被濫用的問題還是沒有真正的治理好,所以后面我們準備自己做一個瓤介。
全新設計的緩存系統(tǒng)——鳳凰
我們新系統(tǒng)起了一個比較高大上的名字吕喘,叫鳳凰,愿景是鳳凰涅磐刑桑,從此緩存不會再死掉了兽泄。
鳳凰是怎么設計的?
在應用中能根據(jù)場景拆分(應用透明)
能從客戶端調(diào)用開始全面監(jiān)控
能防止緩存的崩塌
動態(tài)擴容縮容
自定義客戶端方式與場景配置能力
在支持 Redis 本身的特性的基礎上漾月,我們需要通過自定義的客戶端來實現(xiàn)一些額外的功能。
支持場景配置胃珍,我們考慮根據(jù)場景來管控它的場景梁肿,客戶端每次用 Redis 的時候蜓陌,必須把場景上報給我,你是在哪里吩蔑,用這件事兒是干什么的钮热,雖然這個對于開發(fā)人員來說是比較累的,他往往嵌在它的任務邏輯里面直接跟進去烛芬。曾江場景配置之后隧期,在緩存服務的中心節(jié)點,就可以把它分開赘娄,同一個應用里面兩個比較重要的場景就會不用同一個 Redis仆潮,避免掛的時候兩個一起掛。
同時也需要一個調(diào)度系統(tǒng)遣臼,分開之后性置,不同的 Redis 集群所屬的服務器也需要分開。分開以后我的數(shù)據(jù)怎么復制揍堰,出問題的時候我們怎么把它遷移鹏浅?因此也需要一個復制和遷移的平臺去做。
另外這么一套復雜的東西出來之后屏歹,需要一個監(jiān)控系統(tǒng)隐砸;客戶端里也可以增加本地 cache 的支持。在業(yè)務上也可能需要對敏感的東西進行過濾蝙眶。在底層季希,可以自動實現(xiàn)對訪問數(shù)據(jù)源的切換,對應用是透明的械馆,應用不需要關心真正的數(shù)據(jù)源是什么胖眷,這就是我們自己做的客戶端。
代理層方式
客戶端做了之后還發(fā)生一個問題霹崎,前面分享的唯品會的姚捷說了一個問題珊搀,很多情況下很難升級客戶端。再好的程序員寫出來的東西還是有 bug尾菇,如果 Redis 組件客戶端發(fā)現(xiàn)了一個 bug 需要升級境析,但我們線上有幾千個應用分布在多個業(yè)務開發(fā)團隊,這樣導致很難驅(qū)動這么多開發(fā)團隊去升級派诬。另外一個現(xiàn)狀就是是中國在線旅游行業(yè)好像都喜歡用 .net劳淆,我們之前很多的系統(tǒng)也都是 .net 開發(fā),最近我們也把客戶端嵌到 .net默赂,實現(xiàn)了 .net 版本沛鸵,但是由于各種原因,要推動這么多歷史業(yè)務進行改造切換非常麻煩,甚至有些特別老的業(yè)務最后沒法升級曲掰。
因此我們考慮了 proxy 方案疾捍,這些業(yè)務模塊不需要修改代碼,我們的想法就是讓每一個項目的每一個開發(fā)者自己開發(fā)的代碼是干凈的栏妖,不要在他的代碼里面嵌任何的東西乱豆,業(yè)務訪問的就是一個 Redis。
那么我們就做了吊趾,首先它是 Redis 的協(xié)議宛裕,接下來剛才我們在客戶端里面支持的各種場景配置錄在 proxy 里面,實現(xiàn)訪問通道控制论泛。然后再把 Redis 本身沉在我們 proxy 之后揩尸,讓它僅僅變成一個儲存的節(jié)點,proxy 再做一些自己的事情孵奶,比如本地緩存及路由疲酌。冷熱區(qū)分方面,在一些壓力不大的情況下了袁,調(diào)用方看到的還是個 Redis 朗恳,但是其實可能數(shù)據(jù)是存在 RocksDB 里面了。
緩存服務的架構設計
多個小集群 + 單節(jié)點载绿,我們要小集群的部署和快速的部署粥诫,到當時一個集群有問題的時候,快速移到另一個集群崭庸。
以場景劃分集群
實時平衡調(diào)度數(shù)據(jù)
動態(tài)擴容縮容
可擴容能力
流量的快速增加必然帶擴容的需求與壓力
容量與流量的雙擴容
如何做到平滑的擴容怀浆?容量動態(tài)的數(shù)據(jù)遷移(集群內(nèi)部平衡,新節(jié)點增加)怕享;流量超出時的根據(jù)再平衡集群执赡。
多協(xié)議支持
還有一塊老項目是最大的麻煩,同程有很多之前是 memcache 的應用函筋,后來是轉(zhuǎn)到 Redis 去的沙合,但是轉(zhuǎn)出一個問題來了,有不少業(yè)務由于本身事情較多沒有轉(zhuǎn)換成 Redis跌帐,這些釘子戶怎么辦首懈?同時維護這兩個平臺是非常麻煩的,剛才 proxy 就派到用場了谨敛。因為 memcache 本身它的數(shù)據(jù)支持類型是比較少的究履,因此轉(zhuǎn)換比較簡單,如果是一個更復雜的類型脸狸,那可能就轉(zhuǎn)不過來了最仑。所以我們 proxy 就把這些釘子戶給拆掉了,他覺得自己還是在用 memcache,其實已經(jīng)被轉(zhuǎn)成了 Redis盯仪。
管理與可監(jiān)控能力
最后一塊紊搪,我們這樣一個平臺怎么去監(jiān)控它,和怎么去運維它全景?
整體的管制平臺,
運維操作平臺牵囤,讓它可以去操作爸黄,動態(tài)的在頁面上操作做一件事情,
整體監(jiān)控平臺揭鳞。我們從客戶端開始炕贵,到服務器的數(shù)據(jù),全部把它監(jiān)控起來野崇。
自擴容自收縮称开。動態(tài)的自擴容,自收縮乓梨。
一些業(yè)務應用場景
也是用了一些場景鳖轰,比如說同程前兩年沖的比較狠的就是一元門票,大家肯定說搶購扶镀,這個最大的壓力是什么蕴侣,早上的九點半,這是我們系統(tǒng)最大的壓力臭觉,為什么呢昆雀,一塊錢的門票的從你買完票到景區(qū)里面去,這件事情是在九點半集中爆發(fā)的蝠筑,你要說這個是系統(tǒng)掛了入不了園了狞膘,那十幾萬人不把這個景區(qū)打砸了才怪。那個時候系統(tǒng)絕對不能死什乙。搶購沒有關系挽封,入園是我們最大的壓力,
我們是靠新的系統(tǒng)提供了訪問能力及可用性的支持稳强,把類似這種場景支撐下來场仲,其實緩存本身是可以支撐住的,但是如果濫用管理失控退疫,可能碰到這種高可用的需求就廢了渠缕。
還有一個是火車票系統(tǒng),火車票查詢量非常大褒繁,我們這主要是用了新系統(tǒng)的收縮容亦鳞,到了晚上的時候查的人不多了,到了早上的時候特別多,他查詢量是在一個高低跌蕩的燕差,所以我們可以根據(jù)訪問的情況來彈性調(diào)度遭笋。
Q&A
提問:你們監(jiān)控是用官方的,還是用自己開發(fā)的監(jiān)控軟件徒探?
王曉波:我們監(jiān)控是執(zhí)行命令查過來瓦呼,是我們開發(fā)的。
提問:收縮和擴容這塊你們是怎么做的测暗?
王曉波:收縮和擴容央串,其實現(xiàn)在我們 Redis 本身就是 Redis Cluster,直接往里面加節(jié)點碗啄,加完節(jié)點有一個問題质和,要去執(zhí)行一些命令把數(shù)據(jù)搬走,我們就會自動的把它數(shù)據(jù)平衡掉稚字。
上面說的是 Redis 3.0 集群的情況饲宿,還有一些不是 3.0 的怎么辦?我們有一個程序模擬自己的從機拷出來胆描,然后分配到另外兩個點上去瘫想,本來是一臺,我有個程序模擬自己是一個 Redis 從袄友,它同步過來之后就根據(jù)這個數(shù)據(jù)分到另外兩臺里面去殿托,這個我的成本比較低,因為我的 Redis 集群可以做的很小剧蚣。為什么會這樣呢支竹,我們之前也是碰到一些麻煩點,一個機器很大鸠按,但是往往我要的 Redis 沒這么多礼搁,但是最高的時候是每家隔開,對一些小的應用是最好的目尖,但是我服務器那么多馒吴,所以當我們 Redis Cluster 上來之后我們第一個用的。
相關閱讀
(點擊標題可直接閱讀)
360 開源的類 Redis 存儲系統(tǒng):Pika
近千節(jié)點的 Redis Cluster 高可用集群案例:優(yōu)酷藍鯨優(yōu)化實戰(zhàn)
用最少的機器支撐萬億級訪問瑟曲,微博 6 年 Redis 優(yōu)化歷程
Codis 作者黃東旭細說分布式 Redis 架構設計和踩過的那些坑們
本文及本次沙龍相關 PPT 鏈接如下饮戳,也可點擊閱讀原文直接下載
http://pan.baidu.com/s/1nvnOEBf
想更多了解高可用架構沙龍內(nèi)容,請關注「ArchNotes」微信公眾號以閱讀后續(xù)文章洞拨。關注公眾號并回復城市圈可以更及時了解后續(xù)活動信息扯罐。轉(zhuǎn)載請注明來自高可用架構及包含以下二維碼。
高可用架構
改變互聯(lián)網(wǎng)的構建方式
長按二維碼 關注「高可用架構」公眾號