從零實(shí)現(xiàn)一個(gè)榜單

運(yùn)營活動需要實(shí)現(xiàn)一個(gè)投票打榜的功能:用戶通過做任務(wù)獲取票,獲得票之后點(diǎn)擊頁面的“投票”按鈕,給主播加票撮执;打榜頁面會根據(jù)主播的得票數(shù)對主播進(jìn)行排序,展示榜單前N名舷丹。

一抒钱、榜單存儲設(shè)計(jì)

方案1:zset

對于上述榜單功能的設(shè)計(jì),最簡單的方式就是利用redis的zset結(jié)構(gòu)存儲榜單的排名颜凯,key為本次榜單的標(biāo)識符谋币,member存儲主播的id,score存儲主播的票數(shù)症概。但我們都知道zset會有個(gè)存儲的數(shù)量限制蕾额,推薦的最大值是5000,如果說榜單排序的主播個(gè)數(shù)超過上述限制彼城,那此時(shí)需要設(shè)計(jì)更為復(fù)雜的方案诅蝶。

redis zset結(jié)構(gòu)示意圖

方案2:zset+k-v

如果參與榜單排名的主播數(shù)過多退个,只用zset存儲容易導(dǎo)致大key問題,這時(shí)候需要搭配redis k-v存儲所有主播的票數(shù)调炬。具體解決方案如下:

1. 榜單只存儲topN個(gè)主播的票數(shù)语盈,比如榜單頁面需要展示top100的主播票數(shù),N=100即可缰泡;

2. 所有主播的票數(shù)單獨(dú)存儲刀荒,采用redis k-v存儲票數(shù)。key為主播id棘钞,value代表主播的票數(shù)缠借。

我們本次的功能因?yàn)橹鞑?shù)較多,因此采用方案2實(shí)現(xiàn)武翎。

二烈炭、榜單更新

榜單更新方式

方案1:同步更新

同步更新是最簡單的實(shí)現(xiàn)方式,在用戶調(diào)用投票接口時(shí)直接更新主播的票數(shù)和榜單的排名宝恶,但這個(gè)方案直接訪問redis符隙,只適用于活動流量較小的情況。從穩(wěn)定性的角度考慮垫毙,建議走下面的異步更新方案霹疫。

方案2:異步更新

考慮到活動過程中,容易出現(xiàn)突發(fā)流量综芥,同步更新的方式從穩(wěn)定性的考慮角度不建議丽蝎,因此采用異步更新的方式來實(shí)現(xiàn)。

將用戶投票的消息異步發(fā)送kafka膀藐,在consumer內(nèi)異步處理給主播投票的消息屠阻,給主播加票,同時(shí)更新榜單排名额各。

三国觉、數(shù)據(jù)一致性

榜單更新邏輯

基于zset+kv的實(shí)現(xiàn)方式,用戶給主播投票虾啦,我們需要更新兩個(gè)地方的數(shù)據(jù)

1. 更新主播在kv里的票數(shù)

2. 更新主播在zset的票數(shù)

上述的更新過程是兩個(gè)操作麻诀,于是在實(shí)際更新榜單的過程中,可能會導(dǎo)致一些特殊case的出現(xiàn)傲醉,下面我們來分case具體分析下解決方案蝇闭。

case1:并發(fā)更新導(dǎo)致的數(shù)據(jù)不一致

假設(shè)有兩個(gè)用戶A、B同時(shí)對主播進(jìn)行投票+10票硬毕,主播的原始票數(shù)是10呻引;兩個(gè)操作的順序有可能會導(dǎo)致數(shù)據(jù)不一致

并發(fā)更新榜單排名和主播票數(shù)

由圖中可以看到,解決并發(fā)更新最簡單的方式是將上述兩步操作用lua腳本封裝成原子操作昭殉,但是目前公司的redis使用規(guī)范不提供使用lua腳本苞七,因?yàn)槟_本復(fù)雜度不可控藐守,容易造成一些難以定位原因的redis故障挪丢,我們嘗試想一些別的方法蹂风。

主播更新票數(shù)主要分為兩種情況,用戶上榜時(shí)直接更新榜單內(nèi)的票數(shù)乾蓬,用戶不上榜時(shí)更新kv數(shù)據(jù)惠啄,再用新的票數(shù)去更新榜單數(shù)據(jù)。于是最后的方案定位以下四步:

1任内、先假設(shè)主播在榜上撵渡,使用zIncrByParams().xx()指令直接更新榜單上的票數(shù);如果成功死嗦,直接走步驟4趋距;失敗走步驟2;

2越除、失敗說明主播不在榜上节腐,查詢主播當(dāng)前票數(shù)+delta之后,使用zIncrByParams().nx()指令更新榜單上的票數(shù)摘盆;如果更新成功翼雀,說明此時(shí)沒有別人更新,走步驟4孩擂;如果更新失敗狼渊,說明此時(shí)有別人先一步讓主播上榜,走步驟3类垦;

3狈邑、步驟2失敗,說明此時(shí)主播已經(jīng)被更新到榜上了蚤认;重復(fù)步驟1米苹,使用zIncrByParams().xx()指令直接更新榜單上的票數(shù)。

4烙懦、更新kv內(nèi)的主播票數(shù)驱入,zincrBy即可。

這個(gè)方案保證了主播在榜上更新票數(shù)的原子性氯析,對于主播不在榜上的情況對票數(shù)更新加分布式鎖亏较,保證同時(shí)只有一個(gè)線程更新票數(shù),能解決大多數(shù)情況下并發(fā)更新導(dǎo)致的數(shù)據(jù)不一致情況掩缓。

case2: 距離上一名-1

除了上述并發(fā)更新的問題雪情,主播票數(shù)存儲在兩個(gè)地方,如何保證獲取到的主播票數(shù)一致性是一個(gè)問題你辣。這里踩了另外一個(gè)坑巡通。

筆者在展示榜單排序時(shí)尘执,以zset的排序?yàn)闇?zhǔn)獲取主播的排序,但展示票數(shù)時(shí)請求了k-v內(nèi)單獨(dú)存儲的票數(shù)宴凉。上述的實(shí)現(xiàn)在實(shí)際過程中誊锭,出現(xiàn)了一個(gè)意外的case:距離上一名的票數(shù)計(jì)算出來是-1。初始以為是redis更新k-v和更新zset時(shí)有失敗的情況弥锄,但后續(xù)實(shí)際增加監(jiān)控發(fā)現(xiàn)redis的可用性還是很高的丧靡,基本不存在這種情況。

在此假設(shè)基礎(chǔ)上籽暇,最有可能的情況是在更新k-v和更新redis的中間請求了數(shù)據(jù)温治,導(dǎo)致兩部分?jǐn)?shù)據(jù)一致,出現(xiàn)了距離上一名票數(shù)-1的極端case戒悠。

最終的實(shí)現(xiàn)方案是熬荆,榜單頁面展示時(shí)上榜主播的票數(shù)以zset內(nèi)存儲的為準(zhǔn),保證排名和票數(shù)獲取數(shù)據(jù)來源的一致性绸狐,單獨(dú)查詢主播票數(shù)時(shí)以k-v內(nèi)存儲的票數(shù)為準(zhǔn)卤恳,即可避免上述問題的出現(xiàn)。

四六孵、風(fēng)控和安全

榜單漏出的數(shù)據(jù)除了上述問題之外纬黎,還需要考慮數(shù)據(jù)的安全性,比如上榜主播昵稱是否滿足基本的風(fēng)控要求劫窒,需要具有緊急下榜的能力本今。

五、榜單更新的實(shí)時(shí)性

榜單更新我們采用的是異步操作主巍,那么前端頁面展示部分冠息,如何去保證榜單頁面更新的實(shí)時(shí)性呢?

初始以為直接前端mock票數(shù)即可孕索,后來發(fā)現(xiàn)榜單頁面的更新逛艰,除了更新票數(shù)之外,還涉及到排名部分的更新搞旭,如何保證榜單頁面實(shí)時(shí)展示給用戶是個(gè)亟待解決的問題散怖。

我們此處采用的是和前端配合的形式,后端接口榜單整個(gè)異步操作的延時(shí)基本可以保證在1秒以內(nèi)肄渗,前端用戶投票之后延遲1秒去請求接口镇眷,1秒之后請求的接口數(shù)據(jù)基本為新數(shù)據(jù),這個(gè)過程中給用戶的體驗(yàn)基本是實(shí)時(shí)更新了榜單頁面翎嫡。

六欠动、定榜

榜單的話一般會涉及到一個(gè)定榜操作,對于榜單topN的數(shù)據(jù)會發(fā)放一些獎(jiǎng)勵(lì)等等,因此主播和觀眾對于榜單定榜的結(jié)果較為關(guān)注具伍,定榜的實(shí)時(shí)性也是一個(gè)較為重要的問題翅雏。

針對上述投票打榜的活動,投票接口我們會限制只有活動時(shí)間內(nèi)可投票人芽。但因?yàn)槲覀冞x擇的方案是異步更新榜單排序望几,用戶的投票消息會延遲幾秒后處理,所以我們的定榜時(shí)間要延遲于活動的結(jié)束時(shí)間啼肩。那如果消息延遲過大怎么辦呢橄妆,遲遲不頂榜衙伶?

最簡單的方式是設(shè)定一個(gè)最大延時(shí)定榜時(shí)間祈坠,異步處理consumer內(nèi)增加系統(tǒng)時(shí)間判斷,如果系統(tǒng)時(shí)間大于最大延時(shí)定榜時(shí)間的話矢劲,其他的投票消息不處理赦拘,即可達(dá)到定榜的結(jié)果。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芬沉,一起剝皮案震驚了整個(gè)濱河市躺同,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丸逸,老刑警劉巖蹋艺,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異黄刚,居然都是意外死亡捎谨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門憔维,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涛救,“玉大人,你說我怎么就攤上這事业扒〖爝海” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵程储,是天一觀的道長蹭沛。 經(jīng)常有香客問我,道長章鲤,這世上最難降的妖魔是什么摊灭? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮咏窿,結(jié)果婚禮上斟或,老公的妹妹穿的比我還像新娘。我一直安慰自己集嵌,他們只是感情好萝挤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布御毅。 她就那樣靜靜地躺著,像睡著了一般怜珍。 火紅的嫁衣襯著肌膚如雪端蛆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天酥泛,我揣著相機(jī)與錄音今豆,去河邊找鬼。 笑死柔袁,一個(gè)胖子當(dāng)著我的面吹牛呆躲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捶索,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼插掂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腥例?” 一聲冷哼從身側(cè)響起辅甥,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎燎竖,沒想到半個(gè)月后璃弄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡构回,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年夏块,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捐凭。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拨扶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茁肠,到底是詐尸還是另有隱情患民,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布垦梆,位于F島的核電站匹颤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏托猩。R本人自食惡果不足惜印蓖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望京腥。 院中可真熱鬧赦肃,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厅各,卻和暖如春镜撩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背队塘。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工袁梗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人憔古。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓遮怜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親投放。 傳聞我的和親對象是個(gè)殘疾皇子奈泪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容