參考資料
如何打造千萬級Feed流系統(tǒng)
TableStore Timeline:輕松構(gòu)建千萬級IM和Feed流系統(tǒng)
Design the Twitter timeline and search
Feed系統(tǒng)架構(gòu)資料收集
1. 表結(jié)構(gòu)
1.1. Feed 內(nèi)容庫
內(nèi)容庫用于存儲(chǔ)用戶發(fā)送的消息。
列名 | 是否主鍵 | 說明 |
---|---|---|
user_id | yes | 消息發(fā)送者的 id |
message_id | yes | 消息 id诈乒,可以用時(shí)間戳 |
content | no | 消息內(nèi)容 |
other | no | 其他信息 |
1.2. 同步表
同步方式口蝠,常見的方式有三種:
- 推模式(也叫寫擴(kuò)散):和名字一樣,就是一種推的方式拷淘,發(fā)送者發(fā)送了一個(gè)消息后讲衫,立即將這個(gè)消息推送給接收者肥印,但是接收者此時(shí)不一定在線,那么就需要有一個(gè)地方存儲(chǔ)這個(gè)數(shù)據(jù)入愧,這個(gè)存儲(chǔ)的地方我們稱為:同步庫鄙漏。推模式也叫寫擴(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)【佬蓿基于此胳嘲,所以有了推拉結(jié)合模式,大部分用戶的消息都是寫擴(kuò)散扣草,只有大V是讀擴(kuò)散了牛,這樣既控制了資源浪費(fèi),又減少了系統(tǒng)設(shè)計(jì)復(fù)雜度辰妙。但是整體設(shè)計(jì)復(fù)雜度還是要比推模式復(fù)雜鹰祸。
用圖表對比:
對比項(xiàng) | 推模式 | 拉模式 | 推拉結(jié)合模式 |
---|---|---|---|
寫放大 | 高 | 無 | 中 |
讀放大 | 無 | 高 | 中 |
用戶讀取延時(shí) | 毫秒 | 秒 | 秒 |
讀寫比例 | 1:99 | 99:1 | ~50:50 |
系統(tǒng)要求 | 寫能力強(qiáng) | 讀能力強(qiáng) | 讀寫都適中 |
常見系統(tǒng) | Tablestore、Bigtable等LSM架構(gòu)的分布式NoSQL | Redis密浑、memcache等緩存系統(tǒng)或搜索系統(tǒng)(推薦排序場景) | 兩者結(jié)合 |
架構(gòu)復(fù)雜度 | 簡單 | 復(fù)雜 | 更復(fù)雜 |
介紹完同步模式中所有場景和模式后蛙婴,我們歸納下:
- 如果產(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è)后面專門文章介紹。
同步表的結(jié)構(gòu)如下:
列名 | 是否主鍵 | 說明 |
---|---|---|
user_id | yes | 消息接收者用戶ID |
sequence_id | yes | 消息順序ID浅乔,可以使用timestamp + send_user_id倔喂,也可以直接使用Tablestore的自增列。 |
sender_id | no | 消息發(fā)送者的用戶ID |
message_id | no | store_table中的message_id列的值靖苇,也就是消息ID席噩。通過sender_id和message_id可以到store_table中查詢到消息內(nèi)容 |
other | no | 其他信息 |