通用信息流系統(tǒng)的拉模式要如何做?
如何使用拉模式設(shè)計(jì)信息流系統(tǒng)?
所謂拉模式,指的是用戶自行獲取其關(guān)注的所有人的微博,并按照發(fā)布時(shí)間的倒序進(jìn)行排序和整合,從而生成信息流數(shù)據(jù)的方法。在設(shè)計(jì)微博信息流系統(tǒng)時(shí),會(huì)發(fā)現(xiàn)用戶的收件箱不再必要,因?yàn)樾畔⒘鲾?shù)據(jù)不再源自收件箱,而是來自發(fā)件箱。發(fā)件箱中包含了用戶關(guān)注的所有人數(shù)據(jù)的整合。因此,用戶在發(fā)布微博時(shí)只需將其寫入自己的發(fā)件箱,而不再需要將其推送給粉絲的收件箱。這意味著在獲取信息流時(shí),需要查詢發(fā)件箱的數(shù)據(jù)
這個(gè)邏輯我還用 SQL 的形式直觀地表達(dá)出來,方便你理解。假設(shè)用戶 A 關(guān)注了用戶 B、C、D,那么當(dāng)用戶 B 發(fā)送一條微博的時(shí)候,他會(huì)執(zhí)行這樣的操作:
insert into outbox(userId, feedId, create_time) values("B", $feedId, $current_time); //寫入B的發(fā)件箱
當(dāng)用戶 A 想要獲取他的信息流的時(shí)候,就要聚合 B、C、D 三個(gè)用戶收件箱的內(nèi)容了:
select feedId from outbox where userId in (select userId from follower where fanId = "A") order by create_time desc
確實(shí),拉模式相較于推模式具有明顯的優(yōu)勢。首先,它解決了推送延遲的問題。在拉模式下,大 V 發(fā)微博時(shí)不再需要將消息推送到每個(gè)粉絲的收件箱,因此消除了推送延遲。其次,存儲(chǔ)成本大幅降低。
在推模式下,每條微博都需要被復(fù)制并寫入到每個(gè)粉絲的收件箱,而在拉模式下,只需保留發(fā)件箱,無需復(fù)制微博數(shù)據(jù),從而降低了存儲(chǔ)成本。最后,拉模式具有更好的功能擴(kuò)展性。例如,如果微博增加了分組功能,用戶想將關(guān)注的 A 和 B 分成一個(gè)單獨(dú)的組,那么 A 和 B 發(fā)布的微博就形成了一個(gè)新的信息流。在拉模式下,只需查詢該分組下所有用戶(即 A 和 B),然后查詢這些用戶的發(fā)件箱,按時(shí)間倒序重新排序聚合即可實(shí)現(xiàn)這個(gè)信息流。
List<Long> uids = getFromGroup(groupId); //獲取分組下的所有用戶
Long<List<Long>> ids = new ArrayList<List<Long>>();
for(Long id : uids) {
ids.add(getOutboxByUid(id)); //獲取發(fā)件箱的內(nèi)容id列表
}
return merge(ids); //合并排序所有的id
對于拉模式而言,盡管在業(yè)務(wù)上關(guān)注數(shù)有上限,但它并非完美無缺的方案。下面是針對拉模式可能存在的問題的優(yōu)化建議:
查詢和聚合成本高: 在拉模式下,需要對多個(gè)發(fā)件箱的數(shù)據(jù)進(jìn)行查詢和聚合,這可能會(huì)導(dǎo)致成本較高。針對這個(gè)問題,可以利用緩存來優(yōu)化。根據(jù)用戶瀏覽信息流的特點(diǎn),可以只緩存最近一段時(shí)間內(nèi)的微博 ID,而不是所有用戶的所有微博。比如,僅緩存每個(gè)用戶最近幾天內(nèi)發(fā)布的微博 ID。這樣,可以減少緩存的存儲(chǔ)成本,并在查詢時(shí)從多個(gè)緩存節(jié)點(diǎn)并行獲取數(shù)據(jù),以加快查詢速度。
緩存節(jié)點(diǎn)帶寬成本高: 緩存節(jié)點(diǎn)的帶寬成本可能會(huì)很高,特別是在高流量情況下。針對這個(gè)問題,可以采取一些優(yōu)化措施。例如,可以考慮對緩存數(shù)據(jù)進(jìn)行壓縮,減少數(shù)據(jù)傳輸量,從而降低帶寬消耗。此外,可以使用更高帶寬的網(wǎng)絡(luò)設(shè)備或增加緩存節(jié)點(diǎn)數(shù)量來提升系統(tǒng)的帶寬處理能力。
推拉結(jié)合的方案是怎樣的?
這個(gè)方案的確是一個(gè)有效的解決方案,可以實(shí)現(xiàn)大 V 用戶微博推送的精準(zhǔn)化,以及活躍用戶的識別和管理。以下是方案中關(guān)鍵點(diǎn)的總結(jié)和潛在的實(shí)施方法:
大 V 用戶識別: 以粉絲數(shù)為判斷標(biāo)準(zhǔn)是合理的方法,超過一定數(shù)量的粉絲可被視為大 V 用戶。這個(gè)閾值可以根據(jù)實(shí)際情況進(jìn)行調(diào)整。一旦識別出大 V 用戶,系統(tǒng)就可以將他們作為特殊對象來處理。
活躍用戶標(biāo)記: 活躍用戶的標(biāo)記是方案的關(guān)鍵??梢酝ㄟ^記錄用戶最近幾天內(nèi)的操作行為來判斷其活躍狀態(tài),如刷新信息流、發(fā)布微博、轉(zhuǎn)發(fā)評論、點(diǎn)贊等。這些操作可以作為活躍用戶的判斷依據(jù)。
活躍粉絲列表管理: 對于大 V 用戶,需要維護(hù)一個(gè)活躍粉絲列表。這個(gè)列表應(yīng)該是定長的,當(dāng)一個(gè)用戶從不活躍變?yōu)榛钴S時(shí),將其加入到相關(guān)大 V 用戶的活躍粉絲列表中。當(dāng)列表長度超過設(shè)定值時(shí),可以采取先進(jìn)先出的策略,移除最早加入的粉絲,以保持列表的有效性。
微博推送和收件箱更新: 對于活躍粉絲,實(shí)時(shí)推送大 V 用戶的微博;對于不活躍粉絲或不在大 V 用戶的活躍粉絲列表中的用戶,將大 V 用戶的微博異步插入到其收件箱中,以保證其信息流數(shù)據(jù)的完整性。
圖片
推拉結(jié)合的方式可以在一定程度上彌補(bǔ)推模式的缺陷,但也帶來了額外的維護(hù)成本。隨著粉絲數(shù)量的增加,活躍粉絲列表的維護(hù)和推送延遲都會(huì)成為系統(tǒng)的瓶頸,這時(shí)候轉(zhuǎn)換為拉模式可能會(huì)更為合適。
在粉絲數(shù)量較大的情況下,拉模式相對于推模式更具可擴(kuò)展性和效率。拉模式不需要維護(hù)活躍粉絲列表,也不需要實(shí)時(shí)判斷用戶的在線狀態(tài),因此可以減少系統(tǒng)的維護(hù)成本和推送延遲。同時(shí),拉模式也更適合應(yīng)對高流量的情況,因?yàn)樗恍枰l繁地向大量用戶推送消息,而是由用戶自行拉取所關(guān)注用戶的微博信息,減輕了系統(tǒng)的壓力。
因此,在粉絲數(shù)量較大、流量不斷增加的情況下,將推拉結(jié)合的方式轉(zhuǎn)換為純粹的拉模式可能會(huì)更好地支撐業(yè)務(wù)的發(fā)展,并提供更穩(wěn)定、高效的服務(wù)。
總結(jié):
在拉模式下,我們只需要保存用戶的發(fā)件箱,用戶的信息流是通過聚合關(guān)注者發(fā)件箱數(shù)據(jù)來實(shí)現(xiàn)的;
拉模式會(huì)有比較大的聚合成本,緩存節(jié)點(diǎn)也會(huì)存在帶寬的瓶頸,所以我們可以通過一些權(quán)衡策略盡量減少獲取數(shù)據(jù)的大小,以及部署緩存副本的方式來抗并發(fā);
推拉結(jié)合的模式核心是只推送活躍的粉絲用戶,需要維護(hù)用戶的在線狀態(tài)以及活躍粉絲的列表,所以需要增加多余的空間成本來存儲(chǔ),這個(gè)你需要來權(quán)衡。拉