超牛逼的 Feed 流系統(tǒng)設(shè)計(jì)!

發(fā)布者的數(shù)據(jù):發(fā)布者產(chǎn)生數(shù)據(jù),然后數(shù)據(jù)需要按照發(fā)布者組織,需要根據(jù)發(fā)布者查到所有數(shù)據(jù),比如微博的個(gè)人頁面、朋友圈的個(gè)人相冊等。
關(guān)注關(guān)系:系統(tǒng)中個(gè)體間的關(guān)系,微博中是關(guān)注,是單向流,朋友圈是好友,是雙向流。不管是單向還是雙向,當(dāng)發(fā)布者發(fā)布一條信息時(shí),該條信息的流動(dòng)永遠(yuǎn)是單向的。
接收者的數(shù)據(jù):從不同發(fā)布者那里獲取到的數(shù)據(jù),然后通過某種順序(一般為時(shí)間)組織在一起,比如微博的首頁、朋友圈首頁等。這些數(shù)據(jù)具有時(shí)間熱度屬性,越新的數(shù)據(jù)越有價(jià)值,越新的數(shù)據(jù)就要排在最前面。
存儲庫:存儲發(fā)布者的數(shù)據(jù),永久保存。
關(guān)注表:用戶關(guān)系表,永久保存。
同步庫:存儲接收者的時(shí)間熱度數(shù)據(jù),只需要保留最近一段時(shí)間的數(shù)據(jù)即可。
設(shè)計(jì)Feed流系統(tǒng)時(shí)最核心的是確定清楚產(chǎn)品層面的定義,需要考慮的因素包括:
產(chǎn)品用戶規(guī)模:用戶規(guī)模在十萬、千萬、十億級時(shí),設(shè)計(jì)難度和側(cè)重點(diǎn)會(huì)不同。
關(guān)注關(guān)系(單向、雙向):如果是雙向,那么就不會(huì)有大V,否則會(huì)有大V存在。
上述是選擇數(shù)據(jù)存儲系統(tǒng)最核心的幾個(gè)考慮點(diǎn),除此之外,還有一些需要考慮的:如何實(shí)現(xiàn)Meta和Feed內(nèi)容搜索?
雖然Feed流系統(tǒng)本身可以不需要搜索,但是一個(gè)Feed流產(chǎn)品必須要有搜索,否則信息發(fā)現(xiàn)難度會(huì)加大,用戶留存率會(huì)大幅下降。
Feed流的順序是時(shí)間還是其他分?jǐn)?shù),比如個(gè)人的喜好程度?
雙向關(guān)系時(shí)由于關(guān)系很緊密,一定是按時(shí)間排序,就算一個(gè)關(guān)系很緊密的人發(fā)了一條空消息或者低價(jià)值消息,那我們也會(huì)需要關(guān)注了解的。
單向關(guān)系時(shí),那么可能就會(huì)存在大V,大V的粉絲數(shù)量理論極限就是整個(gè)系統(tǒng)的用戶數(shù),有一些產(chǎn)品會(huì)讓所有用戶都默認(rèn)關(guān)注產(chǎn)品負(fù)責(zé)人,這種產(chǎn)品中,該負(fù)責(zé)人就是最大的大V,粉絲數(shù)就是用戶規(guī)模。
接下來,我們看看整個(gè)Feed流系統(tǒng)如何設(shè)計(jì)。
微博類
朋友圈類
抖音類
私信類

關(guān)注關(guān)系是單向還是雙向:
如果是單向,那么可能就會(huì)存在大V效應(yīng),同時(shí)時(shí)效性可以低一些,比如到分鐘級別;
如果是雙向,那就是好友,好友的數(shù)量有限,那么就不會(huì)有大V,因?yàn)槊總€(gè)人的精力有限,他不可能主動(dòng)加幾千萬的好友,這時(shí)候因?yàn)殛P(guān)系更精密,時(shí)效性要求會(huì)更高,需要到秒級別。
排序是時(shí)間還是推薦:
用戶對feed流最容易接受的就是時(shí)間,目前大部分都是時(shí)間。
但是有一些場景,是從全網(wǎng)數(shù)據(jù)里面根據(jù)用戶的喜好給用戶推薦和用戶喜好度最匹配的內(nèi)容,這個(gè)時(shí)候就需要用推薦了,這種情況一般也會(huì)省略掉關(guān)注了,相對于關(guān)注了全網(wǎng)所有用戶,比如抖音、頭條等。
確定了產(chǎn)品類型后,還需要繼續(xù)確定的是系統(tǒng)設(shè)計(jì)目標(biāo):需要支持的最大用戶數(shù)是多少?十萬、百萬、千萬還是億?
可靠存儲用戶發(fā)送的消息,不能丟失。否則就找不到自己曾經(jīng)發(fā)布到朋友圈狀態(tài)了。
讀取某個(gè)人發(fā)布過的所有消息,比如個(gè)人主頁等。
數(shù)據(jù)永久保存。
數(shù)據(jù)可靠、不丟失。
由于數(shù)據(jù)要永久保存,數(shù)據(jù)會(huì)一直增長,所以要易于水平擴(kuò)展。

對于可靠性,分布式NoSQL的可靠性要高于關(guān)系型數(shù)據(jù)庫,這個(gè)可能有違很多人的認(rèn)知。主要是關(guān)系型數(shù)據(jù)庫發(fā)展很長時(shí)間了,且很成熟了,數(shù)據(jù)放在上面大家放心,而分布式NoSQL數(shù)據(jù)庫發(fā)展晚,使用的并不多,不太信任。但是,分布式NoSQL需要存儲的數(shù)據(jù)量更多,對數(shù)據(jù)可靠性的要求也加嚴(yán)格,所以一般都是存儲三份,可靠性會(huì)更高。目前在一些云廠商中的關(guān)系型數(shù)據(jù)庫因?yàn)椴捎昧撕头植际絅oSQL類似的方式,所以可靠性也得到了大幅提高。
水平擴(kuò)展能力:對于分布式NoSQL數(shù)據(jù)庫,數(shù)據(jù)天然是分布在多臺機(jī)器上,當(dāng)一臺機(jī)器上的數(shù)據(jù)量增大后,可以通過自動(dòng)分裂兩部分,然后將其中一半的數(shù)據(jù)遷移到另一臺機(jī)器上去,這樣就做到了線性擴(kuò)展。而關(guān)系型數(shù)據(jù)庫需要在擴(kuò)容時(shí)再次分庫分表。
如果是自建系統(tǒng),且不具備分布式NoSQL數(shù)據(jù)庫運(yùn)維能力,且數(shù)據(jù)規(guī)模不大,那么可以使用MySQL,這樣可以撐一段時(shí)間。
如果是基于云服務(wù),那么就用分布式NoSQL,比如Tablestore或Bigtable。
如果數(shù)據(jù)規(guī)模很大,那么也要用分布式NoSQL,否則就是走上一條不歸路。


推模式(也叫寫擴(kuò)散):和名字一樣,就是一種推的方式,發(fā)送者發(fā)送了一個(gè)消息后,立即將這個(gè)消息推送給接收者,但是接收者此時(shí)不一定在線,那么就需要有一個(gè)地方存儲這個(gè)數(shù)據(jù),這個(gè)存儲的地方我們稱為:同步庫。推模式也叫寫擴(kuò)散的原因是,一個(gè)消息需要發(fā)送個(gè)多個(gè)粉絲,那么這條消息就會(huì)復(fù)制多份,寫放大,所以也叫寫擴(kuò)散。這種模式下,對同步庫的要求就是寫入能力極強(qiáng)和穩(wěn)定。讀取的時(shí)候因?yàn)橄⒁呀?jīng)發(fā)到接收者的收件箱了,只需要讀一次自己的收件箱即可,讀請求的量極小,所以對讀的QPS需求不大。歸納下,推模式中對同步庫的要求只有一個(gè):寫入能力強(qiáng)。
拉模式(也叫讀擴(kuò)散):這種是一種拉的方式,發(fā)送者發(fā)送了一條消息后,這條消息不會(huì)立即推送給粉絲,而是寫入自己的發(fā)件箱,當(dāng)粉絲上線后再去自己關(guān)注者的發(fā)件箱里面去讀取,一條消息的寫入只有一次,但是讀取最多會(huì)和粉絲數(shù)一樣,讀會(huì)放大,所以也叫讀擴(kuò)散。拉模式的讀寫比例剛好和寫擴(kuò)散相反,那么對系統(tǒng)的要求是:讀取能力強(qiáng)。另外這里還有一個(gè)誤區(qū),很多人在最開始設(shè)計(jì)feed流系統(tǒng)時(shí),首先想到的是拉模式,因?yàn)檫@種和用戶的使用體感是一樣的,但是在系統(tǒng)設(shè)計(jì)上這種方式有不少痛點(diǎn),最大的是每個(gè)粉絲需要記錄自己上次讀到了關(guān)注者的哪條消息,如果有1000個(gè)關(guān)注者,那么這個(gè)人需要記錄1000個(gè)位置信息,這個(gè)量和關(guān)注量成正比的,遠(yuǎn)比用戶數(shù)要大的多,這里要特別注意,雖然在產(chǎn)品前期數(shù)據(jù)量少的時(shí)候這種方式可以應(yīng)付,但是量大了后就會(huì)事倍功半,得不償失,切記切記。
推拉結(jié)合模式:推模式在單向關(guān)系中,因?yàn)榇嬖诖骎,那么一條消息可能會(huì)擴(kuò)散幾百萬次,但是這些用戶中可能有一半多是僵尸,永遠(yuǎn)不會(huì)上線,那么就存在資源浪費(fèi)。而拉模式下,在系統(tǒng)架構(gòu)上會(huì)很復(fù)雜,同時(shí)需要記錄的位置信息是天量,不好解決,尤其是用戶量多了后會(huì)成為第一個(gè)故障點(diǎn)?;诖耍杂辛送评Y(jié)合模式,大部分用戶的消息都是寫擴(kuò)散,只有大V是讀擴(kuò)散,這樣既控制了資源浪費(fèi),又減少了系統(tǒng)設(shè)計(jì)復(fù)雜度。但是整體設(shè)計(jì)復(fù)雜度還是要比推模式復(fù)雜。關(guān)注微信訂閱號碼匠筆記,回復(fù)架構(gòu)獲取一些列的架構(gòu)知識。

如果產(chǎn)品中是雙向關(guān)系,那么就采用推模式。
如果產(chǎn)品中是單向關(guān)系,且用戶數(shù)少于1000萬,那么也采用推模式,足夠了。
如果產(chǎn)品是單向關(guān)系,單用戶數(shù)大于1000萬,那么采用推拉結(jié)合模式,這時(shí)候可以從推模式演進(jìn)過來,不需要額外重新推翻重做。
永遠(yuǎn)不要只用拉模式。
如果是一個(gè)初創(chuàng)企業(yè),先用推模式,快速把系統(tǒng)設(shè)計(jì)出來,然后讓產(chǎn)品去驗(yàn)證、迭代,等客戶數(shù)大幅上漲到1000萬后,再考慮升級為推拉集合模式。
如果是按推薦排序,那么是另外的考慮了,架構(gòu)會(huì)完全不一樣,這個(gè)后面專門文章介紹。


用戶詳情和列表。
關(guān)注或好友關(guān)系。
推送session池。

如果已經(jīng)有了關(guān)系型數(shù)據(jù)庫了,且數(shù)據(jù)量較少,則選擇關(guān)系型數(shù)據(jù)庫,比如MySQL等。
如果數(shù)據(jù)量比較大,這個(gè)時(shí)候就有兩種選擇:
使用具有索引的系統(tǒng),比如云上的Tablestore,更簡單,吞吐更高,擴(kuò)容能力也一并解決了。
需要分布式事務(wù),可以采用支持分布式事務(wù)的系統(tǒng),比如分布式關(guān)系型數(shù)據(jù)庫。


如果需要查詢某個(gè)人的粉絲列表:使用TermQuery查詢固定user_id,且按照timestamp排序。
如果需要查詢某個(gè)人的關(guān)注列表:使用TermQuery查詢固定follow_user_id,且按照timestamp排序。
當(dāng)前數(shù)據(jù)寫入Table后,需要5~10秒鐘延遲后會(huì)在多元索引中查詢到,未來會(huì)優(yōu)化到2秒以內(nèi)。

如果系統(tǒng)中已經(jīng)有了分布式NoSQL數(shù)據(jù)庫,比如Tablestore、Bigtable等,那么直接用這些即可。
如果沒有上述系統(tǒng),那么如果有MySQL等關(guān)系型數(shù)據(jù)庫,那就選關(guān)系型數(shù)據(jù)庫即可。
如果選擇了Tablestore,那么“評論表”設(shè)計(jì)結(jié)構(gòu)如下:


微博中的搜索用戶。
搜索微博內(nèi)容。
微信中搜索好友等。
如果存儲庫使用了MySQL或者Tablestore,那么直接選擇這兩個(gè)系統(tǒng)就可以了。
如果整個(gè)系統(tǒng)都沒使用MySQL、Tablestore,且已經(jīng)使用了搜索系統(tǒng),那么可以直接復(fù)用搜索系統(tǒng),其他場景都不應(yīng)該再額外加一個(gè)搜索系統(tǒng)進(jìn)來,徒添復(fù)雜度。
如果需要對用戶名支持搜索,那么需要對user_table建立多元索引,其中的nick_name需要是Text類型,且單字分詞。
如果需要對Feed流內(nèi)容支持搜索,那么需要對存儲庫表:store_table建立多元索引,這樣就能直接對Feed流內(nèi)容進(jìn)行各種復(fù)雜查詢了,包括多條件篩選、全文檢索等。

開源組件組成的組合系統(tǒng):包括MySQL、Redis、HBase等,這些系統(tǒng)單個(gè)都不能解決Feed流系統(tǒng)中遇到的問題,需要組合在一起,各司其職才能完成一個(gè)Feed流系統(tǒng),適用于熱衷開源系統(tǒng),人多且喜歡運(yùn)維操作的團(tuán)隊(duì)。
Tablestore單系統(tǒng):只使用Tablestore單個(gè)系統(tǒng)就能解決上述的所有問題,這時(shí)候肯定有人要問?你是不是在吹牛?這里不是吹牛,Tablestore在三年前就已經(jīng)開始重視Feed流類型業(yè)務(wù),之前也發(fā)表過多篇文章介紹,功能上也在專門為Feed流系統(tǒng)特別定制設(shè)計(jì),所以到今天,只使用Tablestore一款產(chǎn)品,是可以滿足上述需求的。選擇Tablestore做Feed流系統(tǒng)的用戶具有以下一些特征:
產(chǎn)品設(shè)計(jì)目標(biāo)規(guī)模大,千萬級或億級。
不喜歡運(yùn)維,喜歡專注于開發(fā)。
高效率團(tuán)隊(duì),希望盡快將產(chǎn)品實(shí)現(xiàn)落地。
希望一勞永逸,未來系統(tǒng)隨著用戶規(guī)模增長可以自動(dòng)擴(kuò)容。
希望能按量付費(fèi),用戶少的時(shí)候費(fèi)用低,等用戶增長起來后費(fèi)用在跟隨用戶數(shù)增長。
如果具有上述四個(gè)特征的任何一個(gè),那么都是適合于用Tablestore。
后臺回復(fù)?學(xué)習(xí)資料?領(lǐng)取學(xué)習(xí)視頻
如有收獲,點(diǎn)個(gè)在看,誠摯感謝
