微博技术专家陈波:百亿级访问量的应用如何做缓存架构设计
中生代技術
鏈接技術大咖,分享技術干貨
全文:5588字28圖
閱讀時間:14分鐘
接力技術,鏈接價值
-----------------------------------------------------
鏈接3000+技術總監/CTO, 每天早上推送技術干貨文章
微博日活躍用戶1.6億+,每日訪問量達百億級,面對龐大用戶群的海量訪問,良好架構且不斷改進的緩存體系具有非常重要的支撐作用。
中生代技術走進盒子科技的現場技術交流活動上,新浪微博技術專家陳波為大家講解了微博Cache架構的設計實踐過程。
刷微博嗎?跟我們一起聽聽那些龐大的數據是如何呈現的吧!
陳波:大家好,今天的分享主要有以下內容,首先是微博在運行過程中的數據挑戰,然后是Feed系統架構,接下來會著重分析Cache架構及演進,最后是總結、展望。
數據挑戰
Feed平臺系統架構
總共分為五層,最上層是端層,比如web端,客戶端,大家用的ios或安卓的一些客戶端,還有一些開放平臺,第三方接入的一些接口。下面是平臺接入層,不同的池子,主要是為了把好的資源集中調配給重要的核心接口,這樣突發流量的時候,有更好的彈性來服務,提高服務穩定性。再下面是平臺服務層,主要是Feed算法,關系等等。接下來是中間層,通過各種中間介質提供一些服務。最下面一層就是存儲層,平臺架構大概是這樣。
1. Feed timeline
大家日常刷微博的時候,比如在主站或客戶端點一下刷新,最新獲得了十到十五條微博,它這個是怎么構建出來的呢?刷新之后,首先會獲得用戶的關注關系,比如她有一千個關注,會把這一千個ID拿到,根據這一千個UID,拿到每個用戶發表的一些微博,同時會獲取這個用戶的Inbox,就是她收到的特殊的一些消息,比如分組的一些微博,群的微博,下面她的關注關系,她關注人的微博列表,拿到這一系列微博列表之后進行集合、排序,拿到所需要的那些ID,再對這些ID去取每一條微博ID對應的微博內容,如果這些微博是轉發過來的,它還有一個原微博,會進一步取原微博內容,通過原微博取用戶信息,進一步根據用戶的過濾詞,對這些微博進行過濾,過濾掉用戶不想看到的微博,留下這些微博后,再進一步來看,用戶對這些微博有沒有收藏、贊,做一些flag設置,最后還會對這些微博各種計數,轉發、評論、贊數進行組裝,最后才把這十幾條微博返回給用戶的各種端。這樣看,用戶一次請求,最終得到十幾條記錄,后端服務器大概要對幾百甚至幾千條數據進行實時組裝,再返回給用戶,整個過程對Cache體系強度依賴。所以Cache架構設計優劣直接會影響到微博體系表現的好壞。
2. Feed Cache架構
然后我們看一下Cache架構,它主要分為6層,首先是Inbox,主要是分組的一些微博,然后直接對群主的一些微博,Inbox比較少,主要是推的方式。然后對于Outbox,每個用戶都會發常規的微博,都會在它Outbox里面去,根據存的ID的數量,實際上分成多個Cache,普通的大概是200多,如果是長的大概是2000條。第三組就是一些關系,它的關注、粉絲、用戶。第四個就是內容,每一條微博一些內容存在這里。下面就是一些存在性判斷,比如微博里面,這條微博有沒有贊過,之前有一些明星就說我沒有點贊這條微博怎么顯示我點贊了,引發一些新聞,這種就是記錄,實際上她在某個時候點贊忘記了。最下面還有比較大的一塊——計數。一條微博評論轉發等計數,對用戶來說,她的關注數粉絲數這些數據。
Cache架構及演進
1. 簡單KV數據類型
接下來我們著重講一些微博Cache架構演進過程,最開始微博上線的時候,都是把它作為一個簡單的KV證人數據類型來存儲,我們主要采取哈希分片存儲在MC池子里,上線幾個月之后發現一些問題,有一些節點機器宕機或者其它方面原因,大量的請求會穿透Cache層達到DB上去,導致整個請求變慢,甚至DB僵死。于是我們很快給它改造增加一個HA層,這樣即便Main層出現某些節點宕機情況或者掛掉之后,這些請求會進一步穿透到HA層,不會穿透DB層,這樣的話可以保證在任何情況下,整個系統命中率不會降低,系統服務穩定性比較大提升。對于這種,現在業界用得比較多,然后很多人說我直接用哈希,但這里面也有一些坑,比如我有一個節點,節點3它宕機了,Main把它給摘掉了,節點3的一些QA分給其他幾個節點,這個業務量還不是很大,穿透DB,DB可以抗住。如果后面這個節點3又恢復了,它又加進來,加進來之后,節點3的訪問又會回來,如果節點3因為網絡原因或者機器本身的原因,它又宕機了,一些節點3的請求又會分給其他節點,這個時候就會出現問題,之前分散給其他節點寫回來的數據已經沒有人更新了,如果它沒有被剔除掉就會出現混插數據。
微博和微信很大的區別,實際上微博是一個廣場型的業務,比如突發事件,某明星找個女朋友,瞬間流量就30%,突發事件后,大量的請求會出現在某一些節點,會導致這個節點非常熱,即便是MC也沒辦法滿足這么大的請求量。這時候整個MC就會變成瓶頸,導致整個系統變慢,基于這個原因我們引入L1層,還是一個Main關系池,每一個L1大概是Main層的N分之一,六分之一、八分之一、十分之一這樣一個內存量,根據請求量我會增加4到8個L1,這樣所有的請求來了之后首先會訪問L1,L1命中的話就會直接訪,如果沒有命中再來訪問Main-HA層,這樣在一些突發流量的時候,可以由L1來抗住大部分熱的請求。對微博本身來說,新的數據就會越熱,只用增加很少一部分內存就會抗住更大的量。
簡單總結一下,通過簡單KV數據類型的存儲,我們實際上以MC為主的,層內HASH節點不漂移,Miss穿透到下一層去讀取。通過多組L1讀取性能提升,對峰值、突發流量能夠抗住,而且成本會大大降低。對讀寫策略,采取多寫,讀的話采用逐層穿透,如果Miss的話就進行回寫,對存在里面的數據,我們最初采用Json/xml,12年之后就直接采用Protocol| Buffer格式,對一些比較大的用QuickL進行壓縮。
2. 集合類數據
剛才講到簡單的QA數據,對于復雜的集合類數據怎么來處理,比如我關注了2000人,新增一個人,這就涉及到部分修改。有一種方式把2000個ID全部拿下來進行修改,這種對帶寬、機器壓力會更大。還有一些分頁獲取,我存了2000個,只需要取其中的第幾頁,比如第二頁,也就是第十到第二十個,能不能不要全量把所有數據取回去。還有一些資源的聯動計算,會計算到我關注的某些人里面ABC也關注了用戶D,這種涉及到部分數據的修改、獲取,包括計算,對MC來說它實際上是不太擅長的。各種關注關系都存在Redis里面取,通過Hash分布、儲存,一組多存的方式來進行讀寫分離。現在Redis的內存大概有30個T,每天都有2-3萬億的請求。
在使用Redis的過程中實際上還是遇到其他一些問題,比如從關注關系,我關注了2000個UID,有一種方式是全量存儲,但微博有大量的用戶,有些用戶登陸比較少,有些用戶特別活躍,這樣全部放在內存里面成本開銷是比較大的。所以我們就把Redis使用改成Cache,比如只存活躍的用戶,如果你最近一段時間沒有活躍之后,會把你從Redis里面踢掉,再次有訪問到你的時候把你加進來。這時候存在一個問題,Redis工作機制是單線程模式,如果它加某一個UV,關注2000個用戶,可能擴展到兩萬個UID,兩萬個UID塞回去基本上Redis就卡住了,沒辦法提供其他服務。所以我們擴展一種新的數據結構,兩萬個UID直接開了端,寫的時候直接依次把它寫到Redis里面去,讀寫的整個效率就會非常高,它的實現是一個long型的開放數組,通過Double Hash進行尋址。
對Redis來說我們進行了一些其他的擴展,之前的一些分享,大家在網上也會看到,把數據放到公共變量里面,整個升級過程,我們測試1G的話加載要10分鐘,10G大概要十幾分鐘以上,現在是毫秒級升級。對于AOF,我們采用滾動的AOF,每個AOF是帶一個ID的,達到一定的量再滾動到下一個AOF里面去。對RDB落地的時候,我們會記錄構建這個RDB時,AOF文件以及它所在的位置,通過新的RDB、AOF擴展模式,實現全增量復制。
3. 其他數據類型-計數
接下來還有一些其他的數據類型,比如一個計數,實際上計數在每個互聯網公司都可能會遇到,對一些中小型的業務來說,實際上MC和Redis足夠用的,但在微博里面計數出現了一些特點,單條Key有多條計數,比如一條微博,有轉發數、評論數、還有點贊,一個用戶有粉絲數、關注數等各種各樣的數字,因為是計數,它的Value size是比較小的,根據它的各種業務場景,大概就是2-8個字節,一般4個字節為多,然后每日新增的微博大概十億條記錄,總記錄就更可觀了,然后一次請求,可能幾百條計數要返回去。
4. 計數器-Counter Service
最初是可以采取Memcached,但它有個問題,如果計數超過它內容容量的時候,它會導致一些計數的剔除,宕機或重啟后計數就沒有了。另外可能有很多計數它是為零,那這個時候怎么存,要不要存,存的話就占很多內存。微博每天上十億的計數,光存0都要占大量的內存,如果不存又會導致穿透到DB里面去,對服務的可溶性就會存在影響。2010年之后我們又采用Redis訪問,隨著數據量越來越大之后,發現Redis內存有效負荷還是比較低的,它一條KV大概需要至少65個字節,但實際上我們一個計數需要8個字節,然后Value大概4個字節,實際上有效只有12個字節,其他還有四十多個字節都是被浪費掉的,這還只是單個KV,如果一條Key有多個計數的情況下,它就浪費得更多了,比如說四個計數,一個Key8個字節,四個計數每個計數是4個字節,16個字節大概需要26個字節就行了。但是用Redis存大概需要200多個字節。后來通過自己研發Counter Service,內存降至Redis的五分之一到十五分之一以下,而且進行冷熱分離,熱數據存在內存里面,冷數據如果重新變熱,就把它放到LRU里面去。落地RDB、AOF,實現全增量復制,通過這種方式,熱數據單機可以存百億級,冷數據可以存千億級。
整個存儲架構大概是這樣子,上面是內存,下面是SSD,在內存里面是預先把它分成N個Table,每個Table根據ID的指針序列,劃出一定范圍,任何一個ID過來先找到它所在的Table,如果有直接對它增增減減,有新的計數過來,發現內存不夠的時候,就會把一個小的Table Dump到SSD里面去,留著新的位置放在最上面供新的ID來使用。有些人疑問說,如果在某個范圍內,我的ID本來設的計數是4個字節,但是微博特別熱,超過了4個字節,變成很大的一個計數怎么處理,對于超過限制的把它放在Aux dict進行存放,對于落在SSD里面的Table,我們有專門的IndAux進行訪問,通過RDB方式進行復制。
5. 其他數據類型-存在性判斷
然后除了計數的話,微博還有一些業務,一些存在性判斷,比如一條微博展現的,有沒有點贊、閱讀、推薦,如果這個用戶已經讀過這個微博了,就不要再顯示給他,這種有個很大的特點,它檢查是否存在,每條記錄非常小,比如Value1個bit就可以了,但總數據量巨大。比如微博每天新發表微博1億左右,讀的可能有上百億、上千億這種總的數據需要判斷,怎么來存儲是個很大的問題,而且這里面很多存在性就是0,還是前面說的,0要不要存,如果存了,每天就存上千億的記錄,如果不存,那大量的請求最終會穿透Cache層到DB層,任何DB都沒有辦法抗住那么大的流量。
我們也進行了一些選型,首先直接考慮我們能不能用Redis,單條KV65個字節,一個KV可以8個字節的話,Value只有1個bit,這樣算下來我每日新增內存有效率是非常低的。第二種我們新開發的Counter Service,單條KV Value1個bit,我就存1個byt,總共9個byt就可以了,這樣每日新增內存900G,存的話可能就只能存最新若干天的,存個三天差不多快3個T了,壓力也挺大,但比Redis已經好很多。
我們最終方案采用自己開發Phantom,先采用把共享內存分段分配,最終使用的內存只用120G就可以,算法很簡單,對每個Key可以進行N次哈希,如果哈希的某一個位它是1,如果進行3次哈希,三個數字把它設為1,把X2也進行三次哈希,后面來判斷X1是否存在的時候,進行三次哈希來看,如果都為1就認為它是存在的,如果某一個哈希X3,它的位算出來是0,那就百分百肯定不存在的。
它的實現架構比較簡單,把共享內存預先拆分到不同Table里面,在里面進行開方式計算,然后讀寫,落地的話采用AOF+RDB的方式進行處理。整個過程因為放在共享內存里面,進程要升級重啟數據也不會丟失。對外訪問的時候,建Redis協議,它直接擴展新的協議就可以訪問我們這個服務了。
6. 小結
小結一下,到目前為止,關注Cache集群內高可用、它的擴展性,包括它的性能,還有一個特別重要就是存儲成本,還有一些我們沒有關注到,比如21運維性如何,微博現在已經有幾千差不多上萬臺服務器等等。
7. 進一步優化
8. 服務化
采取的方案首先就是對整個Cache進行服務化管理,對配置進行服務化管理,避免頻繁重啟,另外如果配置發生變更,直接用一個腳本修改一下。
服務化還引入Cluster Manager,實現對外部的管理,通過一個界面來進行管理,可以進行服務校驗。服務治理方面,可以做到擴容、縮容,SLA也可以得到很好保障。另外對于開發來說,現在就可以屏蔽Cache資源。
總結與展望
最后簡單總結一下,對于微博Cache架構來說,從它數據架構、性能、儲存成本、服務化不同方面進行優化增強。
<全文完>
編輯/雪人
最后推薦陳波老師的一個課程
《300分鐘吃透分布式緩存》
推薦閱讀
精彩不容錯過
?
錦官城思享會-成都創業與商業CXO閉門會圓滿舉辦
?
天弘基金首席架構師李鑫:微服務接口限流的算法及架構實現
?
左耳朵耗子:程序員如何把控自己的職業?
?
一文講清K8s如何改變美團的云基礎設施
?
程序員管理思維修煉,只需要反復閱讀本篇
?
螞蟻科技 Service Mesh 落地實踐與挑戰 | GIAC 實錄
?
飛天茅臺超賣P0事故:請慎用Redis分布式鎖!
?
阿里P9專家右軍:大話軟件質量穩定性
?
億級流量請求,多級緩存解救
? ?END ? ?? #接力技術,鏈接價值# 點分享點點贊點在看總結
以上是生活随笔為你收集整理的微博技术专家陈波:百亿级访问量的应用如何做缓存架构设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: List集合中两种遍历方式
- 下一篇: 列表迭代器ListIterator的用法