如何設(shè)計(jì)微信運(yùn)動(dòng)的排行榜功能
騰訊二面的場(chǎng)景題,設(shè)計(jì)微信運(yùn)動(dòng)的排行榜,使用 Redis 的 Zset 數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)。
下面是我自己的分析,歡迎交流和補(bǔ)充,你提的更多問題,我會(huì)添加到文章中,提供給大家去思考。
需求分析:
1.存儲(chǔ)所有用戶的微信步數(shù),使用什么結(jié)構(gòu),key value 分別是啥?
因?yàn)橐鎯?chǔ)每一天的數(shù)據(jù),key 可以是:業(yè)務(wù)名稱加上日期,value 是用戶的 ID、score 就是對(duì)應(yīng)的步數(shù)。
2.不同用戶有不同的好友,每個(gè)人要單獨(dú)實(shí)現(xiàn)一個(gè)排行榜嗎?
不用,只需要維護(hù)一個(gè)排行榜,每一個(gè)用戶都有自己的好友列表,可以用 Set 來存儲(chǔ),拿到好友列表之后,通過 ZScore 拿到好友 ID 的步數(shù),排序之后返回給前端。
3.相同步數(shù)的還有如何排序?微信可能不太重要,但是在游戲中,相同的分?jǐn)?shù)或者王者多少顆星,如果排序呢?
給分?jǐn)?shù) score 賦值的時(shí)候可以帶上一個(gè)時(shí)間戳,這樣就不會(huì)有相同的 socre 存在了。排序的時(shí)候也就解決了這個(gè)問題。
數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì) 每日使用一個(gè) Zset 來存儲(chǔ)用戶當(dāng)天的步數(shù)。
key 設(shè)計(jì): step_rank:{date} 比如 step_rank:20254003.
微信運(yùn)動(dòng)的排行榜對(duì)于業(yè)務(wù)的場(chǎng)景并不是要求實(shí)時(shí)的,比如間隔五分鐘去實(shí)現(xiàn)更新。 可以將步數(shù)先放入到消息隊(duì)列中, Zadd key member value 或者累加操作等,實(shí)現(xiàn)排行榜的變化。
冷熱數(shù)據(jù)隔離
Redis 的內(nèi)存容量也是有限的,不能直接存儲(chǔ)所有天數(shù)的數(shù)據(jù)??梢员A艚斓臄?shù)據(jù),對(duì)于歷史的數(shù)據(jù)都是固定了,可以保存到數(shù)據(jù)庫(kù)里面去,在每天晚上的時(shí)候自動(dòng)將三天前的數(shù)據(jù)備份到數(shù)據(jù)庫(kù)中。
詳細(xì)細(xì)節(jié):
好友排行榜生成:
1.每一個(gè)用戶好友都用 Set 存儲(chǔ),key 為 friends:{userId}
1.獲取用戶123的所有好友ID
SMEMBERS friends:user123
批量的拿到好友的分?jǐn)?shù) 拿到friend1和friend2的分?jǐn)?shù)
ZSCORE step_rank:20231001 friend1
ZSCORE step_rank:20231001 friend2
查詢出來分?jǐn)?shù)存儲(chǔ)到集合中在后端排序完成之后返回給前端進(jìn)行展示
問題
1.如果數(shù)據(jù)量很大,底層的效率如何保證呢?
通過 哈希表+跳表來實(shí)現(xiàn), 通過哈希表 O1 的時(shí)間復(fù)雜度找到跳表中的位置。
跳表不需要平衡樹那樣需要平凡的重新位置平衡,只是需要更新局部節(jié)點(diǎn)。
異步批量的跟新,通過消息隊(duì)列實(shí)現(xiàn)異步更新。
#排行榜##RedisZ#牛牛的面試專欄,希望自己在25年可以拿到一份大廠的SP Offer 你的點(diǎn)贊和收藏都是我持續(xù)更新的動(dòng)力