偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

圖文詳解:如何設(shè)計(jì)一個(gè)億級(jí)用戶排行榜?

開發(fā) 架構(gòu)
雙11零點(diǎn)的銷量榜、游戲版本更新后的戰(zhàn)力榜,峰值QPS可能從日常的 1 萬(wàn)飆升到100萬(wàn)。這時(shí)候不僅要扛住,還要保證P99 延遲 < 200ms,一旦超過這個(gè)閾值,用戶會(huì)明顯感覺"卡頓"。

面試時(shí)被問到"如何設(shè)計(jì)排行榜系統(tǒng)",可別以為是考簡(jiǎn)單的排序算法。

一旦場(chǎng)景升級(jí)到用戶量破億、每秒要處理 10 萬(wàn)次排名查詢,同時(shí)數(shù)據(jù)還得保持分鐘級(jí)更新時(shí),你會(huì)發(fā)現(xiàn)這道題藏著分布式系統(tǒng)設(shè)計(jì)的幾乎所有核心挑戰(zhàn)。

今天牛哥就從需求拆解開始,一步步推導(dǎo)出能支撐億級(jí)用戶的排行榜架構(gòu)。

明確需求

設(shè)計(jì)前不明確核心需求,就像航海沒有指南針。億級(jí)用戶場(chǎng)景下,這四個(gè)指標(biāo)直接決定系統(tǒng)成敗,每個(gè)都要給出量化標(biāo)準(zhǔn)。 

圖片圖片

1. 實(shí)時(shí)性是關(guān)鍵

實(shí)時(shí)性怎么定義才合理?實(shí)測(cè)數(shù)據(jù)告訴我們:

  • 核心榜單(游戲戰(zhàn)力榜)更新延遲超過5秒,用戶投訴率會(huì)上升15%;
  • 非核心榜單(周銷量榜)可放寬到分鐘級(jí),但必須在前端明確提示"5分鐘更新一次"。

這里的關(guān)鍵是用戶感知:哪怕數(shù)據(jù)是異步更新的,也要通過前端動(dòng)效讓用戶覺得實(shí)時(shí)生效。  

2. 準(zhǔn)確性是底線

榜單的核心價(jià)值在于傳遞可信信息,要做到「最終一致 + 不丟數(shù)據(jù)」,需要滿足以下要求:

  • 用戶行為必須 100% 接入計(jì)算,確保數(shù)據(jù)無(wú)遺漏;
  • 分?jǐn)?shù)更新需采用原子化處理,避免并發(fā)場(chǎng)景下的計(jì)數(shù)錯(cuò)誤;
  • 系統(tǒng)發(fā)生故障時(shí),數(shù)據(jù)恢復(fù)后仍能準(zhǔn)確追溯完整記錄;
  • 億級(jí)數(shù)據(jù)規(guī)模下,針對(duì)分片間數(shù)據(jù)同步可能產(chǎn)生的短暫不一致,需設(shè)計(jì)數(shù)據(jù)對(duì)賬機(jī)制,進(jìn)行跨分片的分?jǐn)?shù)總和校驗(yàn)。

圖片圖片

3. 抗壓力決定系統(tǒng)能否"活下來(lái)"

雙11零點(diǎn)的銷量榜、游戲版本更新后的戰(zhàn)力榜,峰值QPS可能從日常的 1 萬(wàn)飆升到100萬(wàn)。這時(shí)候不僅要扛住,還要保證P99 延遲 < 200ms,一旦超過這個(gè)閾值,用戶會(huì)明顯感覺"卡頓"。

4. 靈活性關(guān)系到業(yè)務(wù)迭代

業(yè)務(wù)方經(jīng)常提需求 "今天要日榜,明天加個(gè)周榜,后天還得支持按「銷量+好評(píng)率」混合排序" 。如果每次調(diào)整都要改代碼、重啟服務(wù),技術(shù)團(tuán)隊(duì)會(huì)被業(yè)務(wù)拖著走,所以設(shè)計(jì)時(shí)要預(yù)留「排序規(guī)則動(dòng)態(tài)配置」能力。比如用JSON配置權(quán)重因子,無(wú)需發(fā)版就能生效。

{"like":2,"comment":3,"share":5}

技術(shù)選型:"扛億級(jí)"的工具組合

需求明確后,下一步是選對(duì)工具。很多同學(xué)一上來(lái)就說"用Redis ZSet",但真實(shí)場(chǎng)景的選型要復(fù)雜得多。這三個(gè)核心工具的組合,直接決定系統(tǒng)的性能天花板。  

Redis ZSet:為實(shí)時(shí)榜單而生

Redis的 ZSet 為什么是實(shí)時(shí)排行榜的首選?看三個(gè)核心特性:  

  • O(logN)的分?jǐn)?shù)更新:ZINCRBY 命令能原子化更新分?jǐn)?shù),億級(jí)數(shù)據(jù)量下依然高效; 
  • 內(nèi)置排序能力:自動(dòng)按score排序,無(wú)需額外計(jì)算;
  • 豐富的范圍查詢:ZREVRANGE(查TOP N)、ZCOUNT(查分?jǐn)?shù)區(qū)間人數(shù))等命令完美匹配排行榜需求;  

這里我們想知道,為什么不用數(shù)據(jù)庫(kù)或搜索引擎呢?

看實(shí)際表現(xiàn)就很清楚:MySQL的ORDER BY在百萬(wàn)級(jí)數(shù)據(jù)時(shí)就會(huì)卡頓;Elasticsearch雖然支持排序,但寫入延遲和資源消耗遠(yuǎn)高于 Redis。  

圖片圖片

對(duì)于90%的實(shí)時(shí)榜單場(chǎng)景,ZSet的性價(jià)比無(wú)人能敵。  

但ZSet有個(gè)致命缺點(diǎn):?jiǎn)蜬ey存儲(chǔ)上限。

Redis 單 Key 的存儲(chǔ)大小建議控制在 100MB 以內(nèi)。但在實(shí)際場(chǎng)景中,如果每個(gè)用戶的榜單數(shù)據(jù)約為 16字節(jié),1 億用戶的 ZSet 總大小就約為 1.6GB,遠(yuǎn)超最佳閾值 ,會(huì)導(dǎo)致持久化慢、主從同步延遲等性能問題。

所以億級(jí)場(chǎng)景必須配合 Redis Cluster 分片,將大Key拆分成多個(gè)小 Key, 存儲(chǔ)在不同節(jié)點(diǎn),每個(gè)分片只存100萬(wàn)用戶,大小約16MB,符合Redis最佳實(shí)踐。  

圖片圖片

定時(shí)任務(wù)+分布式計(jì)算:非實(shí)時(shí)榜單的最優(yōu)解

不是所有榜單都需要實(shí)時(shí)更新。日銷量榜、周熱門榜這類"周期結(jié)算型"榜單,用定時(shí)任務(wù)預(yù)計(jì)算比實(shí)時(shí)計(jì)算節(jié)省 90% 資源。但億級(jí)場(chǎng)景下,普通定時(shí)任務(wù)框架不夠用,需要分布式計(jì)算引擎配合。  

XXL-Job + Spark/Flink 怎么選?看數(shù)據(jù)量和計(jì)算復(fù)雜度:  

  • XXL-Job + 分片執(zhí)行:適合數(shù)據(jù)量中等(千萬(wàn)級(jí))、計(jì)算邏輯簡(jiǎn)單的場(chǎng)景。比如全平臺(tái)日銷量榜,數(shù)據(jù)量不算很大。這種方案支持控制臺(tái)暫停/恢復(fù)任務(wù),失敗重試策略也很完善。
  • Spark/Flink 批處理:適合億級(jí)數(shù)據(jù)規(guī)模、計(jì)算邏輯復(fù)雜的場(chǎng)景。以內(nèi)容熱度周榜為例,計(jì)算過程中需要關(guān)聯(lián)用戶畫像和時(shí)間衰減因子, Spark 的 DataFrame API 能夠高效處理這類多表關(guān)聯(lián)計(jì)算。

圖片

實(shí)際項(xiàng)目中建議分層計(jì)算:核心日榜采用 XXL-Job,因其對(duì)實(shí)時(shí)性要求較高;歷史月榜選用 Spark 批處理,依托凌晨時(shí)段進(jìn)行計(jì)算,能有效降低資源成本。

多存儲(chǔ)體系:冷熱數(shù)據(jù)的分層存儲(chǔ)

Redis適合存熱數(shù)據(jù),比如實(shí)時(shí)榜、近7天榜單,但歷史數(shù)據(jù)如用戶歷史排名記錄,需要長(zhǎng)期存儲(chǔ),這時(shí)候要構(gòu)建多級(jí)存儲(chǔ)體系,降低成本。  

數(shù)據(jù)類型

存儲(chǔ)介質(zhì)

存儲(chǔ)周期

訪問延遲

月成本(1億條數(shù)據(jù))

熱數(shù)據(jù)

Redis Cluster

7天

<1ms

約5萬(wàn)元(100GB內(nèi)存)

溫?cái)?shù)據(jù)

MySQL分表

3個(gè)月

<100ms

約5000元(100GB SSD)

冷數(shù)據(jù)

ClickHouse/Hive

永久

<1s

約500元(1TB HDD)

架構(gòu)拆解:四層核心邏輯

工具選好了,接下來(lái)是搭架構(gòu),一個(gè)能抗住億級(jí)用戶的系統(tǒng),一定是職責(zé)分明的。從用戶行為產(chǎn)生到最終展示,排行榜系統(tǒng)可拆成 "數(shù)據(jù)接入層 - 計(jì)算排序?qū)?- 存儲(chǔ)層 - 展示層" 四層架構(gòu):

圖片

每層專注解決一類問題,這樣即使流量翻10倍,也能通過分層擴(kuò)容扛住。  

數(shù)據(jù)接入層:消息隊(duì)列 + 多活部署

用戶的每次點(diǎn)擊、購(gòu)買、點(diǎn)贊,都是榜單數(shù)據(jù)的源頭。億級(jí)場(chǎng)景下,數(shù)據(jù)接入層的目標(biāo)是「不丟數(shù)據(jù)、低延遲、高可用」,單Kafka集群不夠用,需要多活部署 + 異地容災(zāi)。

比如電商平臺(tái)的「全國(guó)商品銷量榜」,用戶廣泛分布于華北、華東、華南三大核心區(qū)域,需在這三個(gè)地域分別部署 Kafka 集群,各區(qū)域用戶行為數(shù)據(jù)直接寫入本地 Kafka 集群;然后通過 Kafka MirrorMaker 工具,將各區(qū)域的商品銷量數(shù)據(jù)跨地域同步。

圖片

數(shù)據(jù)接入層消費(fèi)到 Kafka 中的行為消息后,按照特定規(guī)則計(jì)算出商品銷量的實(shí)時(shí)變動(dòng)。最后調(diào)用榜單系統(tǒng)的分?jǐn)?shù)更新接口,實(shí)現(xiàn)榜單動(dòng)態(tài)刷新。

為什么必須多活?設(shè)想一下:若僅依賴華北地區(qū)的單 Kafka 集群,一旦集群故障,華北用戶的下單、加購(gòu)數(shù)據(jù)將無(wú)法接入,直接導(dǎo)致「全國(guó)商品銷量榜」 缺失華北區(qū)域數(shù)據(jù)。

圖片圖片

而多活部署能確保任一地域集群出現(xiàn)問題時(shí),其他地域集群仍能正常工作,RTO(恢復(fù)時(shí)間目標(biāo))可控制在 5 分鐘內(nèi)。

此外,電商數(shù)據(jù)接入層還需做好兩大關(guān)鍵保障:

  • 流量控制:借助 Kafka 的配額機(jī)制(Quota)限制單個(gè)生產(chǎn)者或消費(fèi)者的流量,例如設(shè)定單商家每秒最多發(fā)送 100 條商品點(diǎn)擊數(shù)據(jù),防范惡意刷流量的攻擊;
  • 死信隊(duì)列(DLQ):專門存儲(chǔ)處理失敗的消息,像訂單 ID 不存在、用戶賬號(hào)已注銷卻產(chǎn)生下單數(shù)據(jù)等異常消息,都會(huì)被導(dǎo)入死信隊(duì)列,后續(xù)可定期安排人工排查處理,避免數(shù)據(jù)丟失。

計(jì)算排序?qū)樱喊磮?chǎng)景選擇架構(gòu)

計(jì)算層是排行榜的"大腦",負(fù)責(zé)把原始分?jǐn)?shù)轉(zhuǎn)化為有序排名。億級(jí)場(chǎng)景下,沒有萬(wàn)能方案,只有按場(chǎng)景選擇的混合架構(gòu)。  

實(shí)時(shí)計(jì)算:基于 Lua 腳本的即時(shí)計(jì)算

適合游戲戰(zhàn)力榜、直播人氣榜這類更新頻率在秒級(jí)、數(shù)據(jù)量中等(千萬(wàn)級(jí))的場(chǎng)景。當(dāng)用戶產(chǎn)生行為后,系統(tǒng)執(zhí)行的命令示例如下:

1. 當(dāng)用戶產(chǎn)生行為后,通過 Lua 腳本直接操作 ZSet 完成原子化更新:

-- 原子化更新分?jǐn)?shù)并返回最新排名(降序排名,戰(zhàn)力高排第1)
local newScore = redis.call('ZINCRBY', 'game_power_ranking:server1', 50, 'user:10086')
local rank = redis.call('ZREVRANK', 'game_power_ranking:server1', 'user:10086') 
return {newScore, rank}

2. 同步更新本地緩存 Caffeine(Java 示例):

// 使用Caffeine更新本地緩存(用戶排名與分?jǐn)?shù))
caffeineCache.put("user:10086:rank", rank);
caffeineCache.put("user:10086:score", newScore);

通過這種方式,既保證了 Redis 集群中分?jǐn)?shù)更新的原子性,又通過 Caffeine 本地緩存提升了后續(xù)查詢效率,讓排名變化能實(shí)時(shí)反饋給用戶。

批量處理:Spark + ClickHouse

適合內(nèi)容熱度周榜、電商月銷量榜這類場(chǎng)景。更新頻率不高,通常是小時(shí)級(jí)或天級(jí),數(shù)據(jù)量卻能達(dá)到億級(jí)規(guī)模。

每天凌晨3點(diǎn),Spark 從 Kafka 消費(fèi)全量行為數(shù)據(jù),結(jié)合用戶畫像、時(shí)間衰減因子等維度算出最終分?jǐn)?shù),之后批量寫入 ClickHouse,再同步到 Redis ZSet 中供查詢。

圖片圖片

混合計(jì)算:Flink 實(shí)時(shí)處理 + 批處理

適合「實(shí)時(shí)+歷史」雙維度的榜單,比如綜合熱度榜,既關(guān)注當(dāng)下的實(shí)時(shí)熱度,也參考 7 天內(nèi)的累計(jì)表現(xiàn),其運(yùn)作流程為:

用戶剛產(chǎn)生的點(diǎn)贊、評(píng)論等即時(shí)行為,會(huì)由 Flink 實(shí)時(shí)捕捉并計(jì)算出對(duì)應(yīng)的實(shí)時(shí)分;到了每天凌晨,Spark 會(huì)啟動(dòng)批處理任務(wù)專門核算歷史分,例如 7 天前的互動(dòng)數(shù)據(jù)影響力會(huì)減弱,權(quán)重調(diào)整為 0.5;

圖片圖片

之后按照 “實(shí)時(shí)分占 70%、歷史分占 30%” 的公式算出綜合分,比如某內(nèi)容實(shí)時(shí)分 80 分、歷史分 60 分,綜合分就是:

80(實(shí)時(shí)分) × 0.7 + 60(歷史分) × 0.3 = 74 分;

而 0.7 和 0.3 這樣的權(quán)重比例會(huì)預(yù)先存在 Redis 的 Hash 結(jié)構(gòu)中,方便靈活調(diào)整,最終的綜合分會(huì)寫入 Redis ZSet,由它完成排序,既保證實(shí)時(shí)性又兼顧歷史數(shù)據(jù)的影響。

存儲(chǔ)層:Redis Cluster + 多級(jí)緩存 + 冷熱分離

存儲(chǔ)層是排行榜的"數(shù)據(jù)底座",在億級(jí)用戶的榜單場(chǎng)景下,存儲(chǔ)層需要同時(shí)滿足查詢快和成本省的需求,核心靠 Redis Cluster 分片、多級(jí)緩存、冷熱分離 這三大策略搭配實(shí)現(xiàn)。

Redis Cluster分片:分而治之

面對(duì)億級(jí)用戶的榜單數(shù)據(jù),我們按用戶 ID 做哈希分片:

  • 把數(shù)據(jù)均勻分成 100 個(gè)分片,用「用戶 ID 除以 100 取余數(shù)」的方式,確定每條數(shù)據(jù)該存入哪個(gè)分片;每個(gè)分片大約存 100 萬(wàn)用戶的榜單數(shù)據(jù),約 16 MB;

圖片圖片

  • 再把這些分片分散到 10 個(gè) Redis 節(jié)點(diǎn)上,每個(gè)節(jié)點(diǎn)負(fù)責(zé) 10 個(gè)分片;

圖片圖片

后續(xù)如果數(shù)據(jù)量增長(zhǎng),只需直接增加 Redis 節(jié)點(diǎn),系統(tǒng)會(huì)自動(dòng)遷移分片到新節(jié)點(diǎn),輕松實(shí)現(xiàn) “橫向擴(kuò)容”。

多級(jí)緩存:讓查詢層層加速

大多數(shù)用戶查榜單,只看熱門內(nèi)容或自己的排名,沒必要每次都查全量數(shù)據(jù)。所以我們用 「三級(jí)緩存」分層承載查詢需求:

  • 本地緩存(Caffeine):直接存在應(yīng)用服務(wù)器的內(nèi)存里,專門緩存 TOP20 的熱門榜單,1 分鐘刷新一次。承載 90% 的首頁(yè)榜單查詢,延遲不到 1 ms,快得像讀本地文件;
  • Redis Cluster:存儲(chǔ) TOP 1000 榜單數(shù)據(jù) + 用戶個(gè)人分?jǐn)?shù),5分鐘刷新一次,承載 10% 的非首頁(yè)查詢;
  • ClickHouse/MySQL:存儲(chǔ)歷史榜單 + 完整排名數(shù)據(jù),按需查詢,如用戶主動(dòng)查看"我的歷史排名"數(shù)據(jù)。

圖片圖片

冷熱分離:給 Redis 減負(fù)

Redis 用內(nèi)存存儲(chǔ),速度快但成本高,而超過 7 天的榜單數(shù)據(jù),用戶查詢頻率會(huì)大幅下降。所以我們每周日凌晨做一次 “冷熱數(shù)據(jù)搬家”。

圖片圖片

將超過7天的實(shí)時(shí)榜數(shù)據(jù)從Redis Cluster 同步到 ClickHouse,同步完成后刪除 Redis 中的歷史數(shù)據(jù),只留下索引方便后續(xù)快速定位。

遷移過程用「雙寫一致性」保證:先寫ClickHouse,成功后再刪Redis,避免數(shù)據(jù)丟失。  

展示層:CDN + API 網(wǎng)關(guān) + 應(yīng)用集群

展示層直接面對(duì)用戶請(qǐng)求,核心目標(biāo)是做到「毫秒級(jí)響應(yīng)、全球低延遲、扛住高并發(fā)」。億級(jí)場(chǎng)景下,單應(yīng)用集群不夠用,需要CDN加速、API網(wǎng)關(guān)限流、多地域應(yīng)用集群三者配合。

CDN 加速靜態(tài)榜單

針對(duì)首頁(yè) TOP20 這類訪問頻率極高的榜單,我們會(huì)先把數(shù)據(jù)生成靜態(tài) JSON 文件,再通過 CDN 分發(fā)到全球各地的節(jié)點(diǎn)。這樣一來(lái),不管用戶在哪個(gè)地區(qū),都能從離自己最近的 CDN 節(jié)點(diǎn)獲取榜單數(shù)據(jù),延遲控制在 50 毫秒以內(nèi),打開頁(yè)面幾乎秒加載。

圖片圖片

為了保證數(shù)據(jù)不過時(shí),我們給 CDN 緩存設(shè)置了 1 分鐘的有效期,同時(shí)借助 API 網(wǎng)關(guān)的主動(dòng)刷新(PURGE)機(jī)制,只要榜單數(shù)據(jù)更新,就能立刻觸發(fā) CDN 節(jié)點(diǎn)緩存同步,既兼顧了速度,又能讓用戶看到最新排名。

API 網(wǎng)關(guān)動(dòng)態(tài)路由

全球用戶的查榜請(qǐng)求,會(huì)先匯總到 API 網(wǎng)關(guān)。它主要做兩件事:

  • 一是動(dòng)態(tài)路由,根據(jù)用戶所在地區(qū),自動(dòng)把請(qǐng)求轉(zhuǎn)發(fā)到最近的應(yīng)用集群,比如北美用戶的請(qǐng)求直接分配到美東集群,進(jìn)一步縮短跨地域訪問的延遲;
  • 二是限流保護(hù),給單個(gè)用戶、單個(gè) IP 設(shè)定訪問上限,比如限制每個(gè)用戶每秒最多查 5 次榜單,避免惡意刷量沖垮后端服務(wù)。

圖片圖片

應(yīng)用集群彈性擴(kuò)容

應(yīng)用集群基于 K8s 部署,搭配 HPA 機(jī)制,也就是水平 Pod 自動(dòng)擴(kuò)縮容,根據(jù)實(shí)際流量自動(dòng)調(diào)整資源。

比如把 CPU 利用率 70% 設(shè)為閾值:當(dāng)超過 70% 時(shí),比如晚間 8-10 點(diǎn)用戶查榜的高峰時(shí)段,集群會(huì)自動(dòng)增加 Pod 數(shù)量,最多能擴(kuò)展到 100 個(gè);而當(dāng)流量回落,CPU 利用率降低時(shí),又會(huì)自動(dòng)縮減 Pod,最低保留 8 個(gè)。

圖片圖片

這樣既保證了高峰期能扛住壓力,又避免了低峰期的資源浪費(fèi)。

關(guān)鍵實(shí)現(xiàn):億級(jí)場(chǎng)景的避坑指南

基礎(chǔ)架構(gòu)搭好后,系統(tǒng)可能能用,但未必扛得住億級(jí)流量。這些關(guān)鍵實(shí)現(xiàn)細(xì)節(jié),決定了系統(tǒng)從及格到優(yōu)秀的差距。  

跨分片查詢:從慢合并到預(yù)計(jì)算加速

查詢?nèi)?TOP100 榜單時(shí),需要從100個(gè)分片中各查 TOP100,得到100×100=10000 個(gè)候選結(jié)果后,再合并排序取 TOP100。

億級(jí)場(chǎng)景下,這個(gè)過程異常耗時(shí),顯然無(wú)法滿足用戶對(duì)"秒開"的需求,需要優(yōu)化。

1. 預(yù)計(jì)算候選集

每個(gè)分片每 5 分鐘預(yù)計(jì)算 TOP1000 的榜單數(shù)據(jù),把這些數(shù)據(jù)緩存到本地,合并時(shí)從每個(gè)分片取 TOP1000,得到100×1000=100000 個(gè)候選結(jié)果。

圖片圖片

雖然數(shù)據(jù)量增加10倍,但能避免"分片內(nèi) TOP100 之外的用戶,實(shí)際可能是全局 TOP100" 的情況。

2. 分布式合并計(jì)算

在應(yīng)用層,我們用 Java 的 PriorityQueue 也就是小頂堆來(lái)合并候選結(jié)果,把堆的大小固定為 100。遍歷所有候選數(shù)據(jù)時(shí),只要當(dāng)前用戶的分?jǐn)?shù)高于堆頂分?jǐn)?shù),就替換堆頂元素,最終堆里剩下的就是全局 TOP100。

不過,100000 條數(shù)據(jù)的合并耗時(shí)約 30ms,再加上 100 次 Redis 查詢(每次約 5ms),總耗時(shí)會(huì)達(dá)到 530ms,不滿足 P99 延遲 ≤200ms 的主流標(biāo)準(zhǔn),依然需要優(yōu)化。  

3. 終極優(yōu)化 — 分層合并

終極解決方案是「分層合并」:先把 100 個(gè)分片按機(jī)架或可用區(qū),分成 10 個(gè)組,每組包含 10 個(gè)分片。

圖片圖片

第一步先在組內(nèi)合并,每個(gè)組算出自己的 TOP1000;第二步再合并 10 個(gè)組的 TOP1000,得到最終的全局 TOP100。

圖片圖片

組內(nèi)合并可以直接在 Redis Proxy 層完成,這樣應(yīng)用層只需發(fā)起 10 次組查詢,再做一次全局合并即可。優(yōu)化后總耗時(shí)降到 80ms(10 次查詢 ×5ms + 合并 30ms),完全滿足億級(jí)場(chǎng)景的響應(yīng)要求。

數(shù)據(jù)一致性:從最終一致到可追溯

億級(jí)場(chǎng)景下,絕對(duì)一致性無(wú)法實(shí)現(xiàn),因?yàn)榭绶制瑢?shí)時(shí)同步成本太高,但要保證「最終一致+可追溯」。具體通過三層策略實(shí)現(xiàn):

實(shí)時(shí)數(shù)據(jù)一致性

主要通過保證單分片原子性,跨分片定期修復(fù)的策略實(shí)現(xiàn):

  • 單分片內(nèi):用 Lua 腳本保證「查詢狀態(tài) + 修改分?jǐn)?shù)」的原子性,確保高并發(fā)下不會(huì)出現(xiàn) “同一用戶重復(fù)通關(guān),導(dǎo)致通關(guān)積分重復(fù)增加” 的臟數(shù)據(jù)。
  • 跨分片間:允許短暫的不一致,但每天凌晨會(huì)啟動(dòng)定期對(duì)賬,比對(duì)各分片的總分,發(fā)現(xiàn)偏差后自動(dòng)修復(fù)。

圖片圖片

歷史數(shù)據(jù)一致性

主要通過「批處理對(duì)賬 + 告警排查」實(shí)現(xiàn)。每天用 Spark 批處理計(jì)算用戶的每日總分,再和 Redis Cluster 中存儲(chǔ)的分?jǐn)?shù)總和對(duì)比,允許誤差控制在 0.1% 以內(nèi);如果超過這個(gè)閾值,就會(huì)觸發(fā)告警,提醒工程師排查原因。

用戶行為可追溯

每一次分?jǐn)?shù)更新操作,都會(huì)記錄詳細(xì)日志,包括用戶 ID、行為類型、分?jǐn)?shù)增減量、操作時(shí)間戳、請(qǐng)求 ID 等。日志通過ELK存儲(chǔ),支持按用戶ID/時(shí)間范圍查詢,當(dāng)用戶投訴分?jǐn)?shù)異常時(shí),工程師能通過日志快速定位問題根源。

圖片圖片

監(jiān)控告警體系:億級(jí)下的可觀測(cè)性

億級(jí)系統(tǒng)"黑盒運(yùn)行"等于裸奔,必須構(gòu)建完善的監(jiān)控告警體系,覆蓋分片健康度、數(shù)據(jù)一致性、性能指標(biāo)。  

1. 分片健康度監(jiān)控

監(jiān)控每個(gè) Redis 分片的QPS、內(nèi)存使用率、響應(yīng)時(shí)間,尤其是響應(yīng)時(shí)間重點(diǎn)關(guān)注 P99、P999 分位值。一旦任一指標(biāo)觸及設(shè)定的閾值,比如 QPS 超過 1 萬(wàn)、內(nèi)存使用率超過 80%、P99 響應(yīng)時(shí)間超過 100ms,就會(huì)立刻發(fā)送告警;

圖片

同時(shí)監(jiān)控分片遷移狀態(tài),要是遷移速度低于 10MB/s,同樣會(huì)觸發(fā)告警,以此避免遷移超時(shí)影響服務(wù)的可用性 。

2. 數(shù)據(jù)一致性監(jiān)控
  • 實(shí)時(shí)監(jiān)控:每分鐘計(jì)算 Redis Cluster 的總分波動(dòng),若當(dāng)前總分與 5 分鐘前的差值超過 10 萬(wàn),立即告警。
  • 離線對(duì)賬:每天凌晨比對(duì) Redis 和 ClickHouse 中的歷史分?jǐn)?shù),誤差超過 0.1% 就觸發(fā)告警,確保歷史數(shù)據(jù)不丟不錯(cuò)。
3. 用戶體驗(yàn)監(jiān)控

通過前端埋點(diǎn)收集榜單加載時(shí)間,P95>500ms 時(shí)觸發(fā)告警,及時(shí)發(fā)現(xiàn)CDN緩存失效、應(yīng)用集群過載等問題。  

總結(jié):億級(jí)排行榜的設(shè)計(jì)心法

設(shè)計(jì)億級(jí)用戶排行榜,本質(zhì)是對(duì)"實(shí)時(shí)性 - 準(zhǔn)確性 - 成本 - 可用性"的四重權(quán)衡。記住這6個(gè)核心原則,無(wú)論面試還是實(shí)戰(zhàn)都能游刃有余:  

1. 大 Key 必須分片,小 Key 優(yōu)化存儲(chǔ):?jiǎn)蝂Set存不下億級(jí)用戶,用Redis Cluster按哈希分片;非熱門數(shù)據(jù)啟用ziplist編碼

2. 實(shí)時(shí)用 Redis+Lua,批處理用 Spark/Flink 

3. 跨分片查詢分層合并:先組內(nèi)合并再全局合并

4. 多級(jí)存儲(chǔ)控成本:熱數(shù)據(jù)、溫?cái)?shù)據(jù)、冷數(shù)據(jù)分層存儲(chǔ)

5. 數(shù)據(jù)一致性可追溯:?jiǎn)畏制硬僮?定期對(duì)賬+行為日志,保證最終一致且問題可追溯

6. 監(jiān)控容災(zāi)不可少:分片健康度、數(shù)據(jù)一致性、用戶體驗(yàn)全鏈路監(jiān)控,確保系統(tǒng)活下來(lái)

最后想說,面試時(shí)被問到這類問題,別再直接說"用Redis ZSet"了,先問清楚業(yè)務(wù)場(chǎng)景,再給出分層方案,這才是面試官想看到的系統(tǒng)設(shè)計(jì)能力。

責(zé)任編輯:武曉燕 來(lái)源: 牛牛碼特
相關(guān)推薦

2025-09-17 10:08:43

2024-04-01 08:13:59

排行榜MySQL持久化

2018-11-01 13:23:02

網(wǎng)關(guān)APIHTTP

2025-03-10 12:10:00

RedisJava排行榜

2024-05-15 17:21:18

RedisSpring數(shù)據(jù)

2018-11-26 08:06:24

API網(wǎng)關(guān)億級(jí)

2019-10-21 10:59:52

編程語(yǔ)言JavaC

2013-08-23 09:41:19

2020-03-03 07:59:29

設(shè)計(jì)秒殺系統(tǒng)

2022-06-17 12:10:07

RPA機(jī)器人流程自動(dòng)化

2020-11-05 16:46:11

智能芯片測(cè)試

2014-07-30 12:56:56

2025-05-07 08:21:01

2022-06-08 13:50:41

AI專業(yè)排行

2022-08-09 08:29:50

TIOBE編程語(yǔ)言排行榜程序員

2024-08-29 09:32:36

2012-04-28 14:29:36

App Store沖榜策略排行榜規(guī)則

2020-03-07 22:01:58

編程語(yǔ)言JavaPython

2012-03-22 14:18:11

大數(shù)據(jù)

2013-04-01 09:50:15

Web框架Web
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)