原文出處:http://weibo.com/p/1001643880172431480781
作者:唐揚,@唐揚TY
未讀提醒功能在各種社交平臺服務中較為常見,在微博中這些功能由Unread服務來提供。看似簡單的功能,當請求量級達到一定規(guī)模后,成本、性能、穩(wěn)定性的平衡將是架構(gòu)設(shè)計的重點。
大綱
在以timeline為核心的微博業(yè)務中, 未讀數(shù)場景出現(xiàn)的頻率較高,它可以是這樣的…
也可以是這樣的…
通過分析和比較這些未讀場景,我們抽象了unread服務中設(shè)計到的三種主要操作:
1.?incr:增加未讀數(shù)
2.?reset:未讀數(shù)清零
3.?get:獲取未讀數(shù)
我們發(fā)現(xiàn)unread服務中g(shù)et操作是典型的無觸發(fā)操作,即不需要用戶執(zhí)行任何操作都會對服務器造成請求。正是這個特點給unread服務帶來如下問題:
1.?高并發(fā):高峰期單一業(yè)務的qps達到10萬+
2.?性能要求高:接口4個9的響應時間在10ms
為了解決上述問題,unread架構(gòu)針對不同的業(yè)務場景設(shè)計了不同的方案,保證了服務的高性能、高可用和可擴展。本文主要針對三種典型的未讀數(shù)場景介紹微博平臺是如何設(shè)計解決方案的。
在這種場景下,用戶的某一個操作只會影響一個用戶的未讀數(shù)字。典型的場景有:@未讀提醒、評論未讀提醒、贊評論提醒等等。
針對這種場景,我們采用最簡單的解決方案:為每一個用戶存儲一份未讀數(shù)字,如下圖
在設(shè)計的實現(xiàn)中,由于存儲容量可控,我們采用redis存儲未讀數(shù)。相比于通常使用的mysql+mc的存儲解決方案,redis有以下的優(yōu)勢:
1.?存儲一體化,避免了緩存和持久化存儲之間一致性的問題
2.?快速恢復
這個設(shè)計方案主要基于如下的考慮:
1.?簡單直觀
2.?性能能夠達到SLA,每次操作只需要訪問一次資源
打點主要指微博官方客戶端中的一些弱提醒功能,見下圖中的紅點
而全量打點指對全量用戶都增加未讀提醒紅點??紤]到目前微博的用戶量,如果采用第一種場景的方案,打點過程會存在很大的延遲。因此我們采用了基于tag的解決方案。
1.?存儲公共的時間戳global
2.?每一個用戶存儲一個時間戳
3.?打點時,更新global時間戳為當前時間
4.?消點時,更新用戶的時間戳為global時間戳
5.?如果用戶時間戳小于global時間戳,則有點;否則沒有點
這個方案適用于用戶間存在共享存儲,且共享存儲有限的場景。在這種場景下,我們?yōu)槊恳粋€用戶存儲一個tag用來記錄用戶在共享存儲中的已讀位置,這樣就可以通過比較這個已讀位置獲得用戶的未讀數(shù)。
在實際的應用過程中,我們通常會使用本地緩存來解決訪問共享存儲的極端峰值。這種基于已讀位置的解決方案雖然能很好的解決全量打點的問題,但是面對訪問量最大的微博未讀數(shù)場景卻是無能為力,原因有二:
1.?用戶的feed是無限的,不存在共享存儲,全部存儲下來的成本很高
2.?在高并發(fā)下獲取未讀數(shù)操作性能衰減嚴重
我們采用了下面這種方案來解決微博未讀數(shù)問題。
眾所周知,微博未讀數(shù)就是微博主feed未讀數(shù),當我關(guān)注的人發(fā)表一條微博,我的微博未讀數(shù)提醒就會加一。
對于微博微博數(shù)場景,我們采用了基于snapshot的解決方案,具體如圖所示:
更多建議: