Fashiolista最初是我們作為興趣在業餘時間開發的一個項目,當初完全沒有想到它會成長為規模如此大的線上時尚交流網站。最早的版本開發用了大概兩周的時間,當時feed資訊流推送系統相當簡單。在這裡分享一些我們擴充feed系統的經驗。
<a href="http://cms.csdnimg.cn/article/201311/07/527b6c004d1cb.jpg" target="_blank"></a>
建構這個feed系統用到了兩個政策:
拉取(Pull),讀取的過程中收集feed。
推送(Push),寫的過程中提前計算好feed。
大多數實時線上應用程式會使用這兩種方法的組合,将動态推送給你的粉絲的過程被稱為消息分發(fanout)。
Fashiolista的feed系統經過了三次重大改進。第一個版本基于PostgreSQL資料庫,第二個版本使用Redis資料庫,目前的版本采用Cassandra資料庫。為了便于讀者更好的了解這些版本更替的時間和原因,筆者會首先介紹一些背景知識。
第一部分——資料庫
select * from love where user_id in (...)
令人驚訝的是這個系統的強健性還不錯。當love(類似于“贊”了某件服飾)的數量達到百萬時,它運作得很好,超過500萬時,依然沒有問題。我們還打賭說這個系統不能支援千萬的數量級,但是當love到達千萬時,它依然運作得很好。這個簡單的系統支撐着我們的系統達到了百萬的使用者和過億的love,期間隻進行了一些小改動。之後随着使用者的增多,這個系統開始出現波動,部分使用者的延時長達數秒,在參考了很多關于feed系統的架構設計之後,我們開發了第一個基于Redis的Feedly。
第二階段——Redis和Feedly
Redis是一個好的解決方案,但是幾個原因迫使我們不得不尋找新的方案。首先,我們希望支援多文檔類型,而Redis傳回資料庫查詢更困難,并且提高了存儲需求。另外,随着業務的增大,資料庫復原也變得越來越慢。這些問題隻能靠在Redis上存儲更多的資料來解決,但是這樣做的成本太高了。
<a href="http://cms.csdnimg.cn/article/201311/08/527c3c37ed964.jpg" target="_blank"></a>
筆者認為Fashiolista設計的改進過程非常有代表性,在建構一個feed系統時(尤其是使用Feedly)有幾個重要的設計問題需要考慮。
1.非規範化Vs規範化
規範化的方法是,你關注的人的feed清單中是每條動态的ID,非規範的存儲是動态的所有資訊。
僅存儲ID可以大幅度減少記憶體消耗,然而這意味着每次加載feed都要重新通路資料庫。如何選擇取決于你在進行非規範化存儲時,複制資料的頻率。比如建構一個消息通知系統和一個feed系統有很大的差別:通知系統中每個動作的發生隻需要被發送給幾個使用者,而feed系統中每個動态的資料可能要被複制給成千上萬的粉絲。
另外,如何選擇取決于你的存儲架構,使用Redis時,記憶體是需要特别注意的問題;而使用Cassandra要占用大量的存儲空間,但是對于規範化資料來說使用并不簡單。
對于feed通知和基于Cassandra建構的feed,筆者建議将你的資料非規範化。而基于Redis的feed你需要最小化記憶體消耗,并保持資料規範化。采用Feedly可以輕松實作兩種方案。
2.基于生産者的選擇性分發
3.基于消費者的選擇性分發
另外一種選擇性分發方式是指對那些活躍使用者(比如過去一周登入過的使用者)分發消息。我們對這個方法進行了修改,為活躍使用者存儲最近的3600條動态,為非活躍使用者存儲180條,讀取180條之後的資料需要重新通路資料庫,這種方式對于非活躍使用者的體驗不太好,但是能有效降低記憶體消耗。
Silberstein等人認為最适合選擇性推送模式的情境是:
生産者偶爾生産動态資訊
消費者經常請求feed
遺憾的是Fashiolista還不需要如此複雜的系統,很好奇業務要達到多少數量級才會需要這種解決方案。
4.優先級
一個替代的政策是在分發任務時采取不同的優先級,将給活躍使用者的分發任務設為高優先級,向非活躍使用者的分發任務設為低優先級。Fashiolista為高優先級的使用者預留了一個較大的緩存空間,來處理随時的峰值。對于低優先級使用者,我們靠自動擴充和點執行個體。在實踐中,這意味着非活躍使用者的feed會有一定的延時。使用優先級降低了明星使用者對系統的負載壓力,雖然沒有解決根本問題,但大幅度降低了系統負載峰值的量級。
5.Redis Vs Cassandra
然而Redis存在一定的限制,所有的資料需要被存儲在RAM中,成本很高。另外,Redis不支援分片,這意味着你必須在結點間分片(Twemproxy是一個不錯的選擇),這種分片很容易,但是添加和删除節點時的資料處理很複雜。當然你可以将Redis作為緩存,然後重新通路資料庫,來克服這個限制。但是随着通路資料庫的成本越來越高,筆者建議還是用Cassandra代替Redis。
在建構自己的feed解決方案時,有很多因素需要在節點分片時考慮:選擇何種存儲架構?如何處理明星使用者帶來的負載峰值?非規範化資料到何種程度?筆者希望借助這篇文章能夠為你提供一些建議。
請注意隻有資料庫中的使用者達到百萬時,你才會需要解決這個問題。在Fashiolista簡單的資料庫解決方案就支撐我們達到了百萬使用者和過億的love。
更多關于feed系統的設計,筆者強烈建議看一下這些文章:
<a href="http://research.yahoo.com/files/sigmod278-silberstein.pdf">Yahoo Research Paper</a>
<a href="http://planetcassandra.org/blog/post/instagram-making-the-switch-to-cassandra-from-redis-75-instasavings">Cassandra at Instagram</a>
<a href="http://www.slideshare.net/danmckinley/etsy-activity-feeds-architecture/">Etsy feed scaling</a>
<a href="http://www.infoq.com/presentations/Facebook-Software-Stack">Facebook history</a>
<a href="http://www.quora.com/What-are-best-practices-for-building-something-like-a-News-Feed?q=news+feeds">Quora post on best practises</a>
<a href="http://www.quora.com/What-are-the-scaling-issues-to-keep-in-mind-while-developing-a-social-network-feed">Quora scaling a social network feed</a>
<a href="http://web.archive.org/web/20130525202810/http://blog.waxman.me/how-to-build-a-fast-news-feed-in-redis">Redis ruby example</a>
<a href="http://backchannel.org/blog/friendfeed-schemaless-mysql">FriendFeed approach</a>
<a href="http://blog.thoonk.com/">Thoonk setup</a>
<a href="http://www.slideshare.net/nkallen/q-con-3770885">Twitter's Approach</a>
如何聯系我:【萬裡虎】www.bravetiger.cn
【QQ】3396726884 (咨詢問題100元起,幫助解決問題500元起)
【部落格】http://www.cnblogs.com/kenshinobiy/