微博feed系統推拉模式和時間分割槽拉模式架構探討
sns系統,微博系統都應用到了feed(每條微博或者sns裡的新鮮事等我們稱作feed)系統,不管是twitter.com或者國內的新浪微博,人人網等,在各種技術社群,技術大會上都在分享自己的feed架構,也就是推拉模式(timyang上次也分享了新浪微薄的模式)。下面我們就微博的feed推拉(push,pull)模式做一下探討,並提出新的時間分割槽拉模式。
眾所周知,在微博中,當你發表一篇微博,那麼所有關注你的followers(粉絲)都會在一定的時間內收到你的微薄,這有點像群發一封郵件,所有的抄送者都會在一定的時間內收到。到這裡,你可能覺得沒有什麼難度。我們看下下面的截圖:
圖1 新浪微博姚晨
圖2 twitter上馮大輝
新浪微博的姚晨粉絲有2594751,她發表任何一篇微博,都需要2594751個粉絲在一定的時間內收到,twitter的馮大輝發表一篇的話,需要19868個followers收到。
相反,姚晨需要收到他關注的545個人的所有更新,馮大輝需要收到他關注的2525個人的所有更新。到這裡,你是不是感覺到有那麼一點點小挑戰呢?
下面我們看下微博一般的整體結構圖:
圖3 微博整體結構
圖中展示了微博的整體資料流程,先了解下整體的資料結構,沒有涉及到followers等的推拉模式處理。下面我們再看下推模式(push):
圖4 推模式結構
推模式需要把一篇微博推送給所有關注他的人(推給所有的粉絲),比如姚晨,我們就需要推送給2594751個使用者的feeds表中。當然,feeds表可以很好的進行sharding,儲存也都是一些數字型的欄位,儲存空間可能不是很大,使用者在查詢自己關注的所有人的feed時,速度快,效能非常高,但是推送量會非常大,姚晨發表一篇,就會產生200多萬條資料。試想,一個大量使用者的微薄系統通過使用推模式,是不是會產生非常驚人的資料呢?
下面看下拉模式(pull)
圖5 拉模式
拉模式只需要使用者發表微博時,儲存一條微博資料到feeds表中(feeds表可以是一個臨時表,只儲存近期可接受範圍的資料).使用者每次查詢feed時都會去查詢feeds表。比如姚晨開啟自己的微薄首頁,就產生:SELECTidFROMfeedswhereuidin(followinguidlist)ORDERBYidDESCLIMITn(查詢最新的n條),快取到memcached
uidlist=>{data:idlist,timeline:上次查詢出來的最新的一條資料的時間}
再次重新整理:SELECTidFROMfeedswhereuidin(followinguidlist)ANDtimeline>(memcached儲存的上次的timeline)ORDERBYidDESCLIMITn
這種模式實現起來也是比較簡單和容易的,只是在查詢的時候需要多考慮下快取的結構。但是feeds表會產生很大的壓力,怎麼說feeds表也要儲存最近十天半個月的資料吧,對於一個大點的系統,這會產生比較大的資料,如果following的人數比較多,資料庫的壓力就會非常大。而且一般線上的使用者,客戶端都會定期掃描,又會增加很大的壓力,這在查詢效能上沒有推模式的效率高。
下面我們在對拉模式做一下改進優化
圖6 拉模式(pull)-改進(時間分割槽拉模式)
拉模式的改進主要是在feeds的儲存上,使用按照時間進行分割槽儲存。分為最近時間段(比如最近一個小時),近期的,比較長時期等等。我們再來看下查詢的流程,比如姚晨登陸微博首頁,假設快取中沒有任何資料,那麼我們可以查詢比較長時期的feeds表,然後進入快取。下一次查詢,通過查詢快取中的資料的timeline,如果timeline還在最近一個小時內,那麼只需要查詢最近一個小時的資料的feed表,最近一個小時的feeds表比圖四的feeds表可要小很多,查詢起來速度肯定快幾個數量級了。
改進模式的重點在於feeds的時間分割槽儲存,根據上次查詢的timeline來決定查詢應該落在那個表。一般情況下,經常線上的使用者,頻繁使用的客戶端掃描操作,經常登入的使用者,都會落在最近的feeds表區間,查詢都是比較高效的。只有那些十天,半個月才登入一次的使用者需要去查詢比較長時間的feeds大表,一旦查詢過了,就又會落在最近時間區域,所以效率也是非常高的。
關於時間的分割槽,需要根據資料量,使用者訪問特點進行一個合理的切分。如果資料發表量非常大,可以進行更多的分割槽。
上面介紹的推模式和拉模式都有各自的特點,個人覺得時間分割槽拉模式彌補了圖四的拉模式的很大的不足,是一個成本比較低廉的解決方案。當然,時間分割槽拉模式也可以結合推模式,根據某些特點來增加系統的效能。
後記:本文的目的是介紹時間分割槽拉模式,本人對新浪微博和twitter等的推拉模式的細節並不清楚。