阿巴可懂的實時排行榜系統設計和實作思路。
大家好,我是魚皮,暑假快到了,我的老弟小阿巴聽說我家有很多好康的,就跑來找我玩。

結果我擺出了幾個以前開發過的小系統,準備在這段時間帶着小阿巴多做些作品,學習程式設計項目的設計思路。這樣等他開學了,就可以更輕松地跟着老師做做項目了。
今天,就先帶他做一個很常見的小功能:使用者實時積分排行榜。
先描述下需求,在我的程式設計導航項目(https://www.code-nav.cn)中,為了鼓勵大家共同維護網站,使用者可以通過推薦資源、積極評論、舉報違規資源等方式擷取積分。
為了進一步激勵大家,網站需要提供一個使用者積分排行榜,分為 實時總積分榜 、 周榜 和 月榜,均 隻取前 10 名 。所有使用者都能夠檢視目前排行榜,以及檢視自己的 實時 總積分排名,後續管理者就可以給上榜使用者頒發獎品了。
效果如下圖:
點選 <code>我的排名</code> 按鈕,可以檢視自己的實時排名:
本文篇幅有限,先僅讨論 實時總積分榜 的設計實作。
聽了需求後,小阿巴爽朗一笑:這有啥難的?且讓我設計一波,再給你娓娓道來。
先看下資料庫的結構,總共有 2 個表:使用者表 和 使用者積分表。
使用者表存儲了使用者資訊,以及使用者的總積分(實時更新),也就是說總積分榜需要的資料可以直接從這裡取到,不需要再去計算。
使用者表内容:
使用者 id
使用者名
積分(score)
1
小阿巴
10
2
李魚皮
1000
3
小李
100
......
李老熱
66
如果要取前 10 名,隻需要把所有使用者的資訊先取出來,再排個序就好啦,寫 SQL 語句查詢的話就是:
然後如果要取自己的總排名,就對查到的有序資料進行一次周遊,找到自己所在的位置下标就行,僞代碼如下:
小阿巴得意到:這不就實作總積分榜了麼?你這需求太簡單,啧啧。
我笑到:還不錯,總積分榜的思路是正确的,起碼知道要對所有的資料進行排序。但如果使用者數特别多呢?比如幾十萬個,你隻需要查自己的總排名,還需要把全部的資料都做一個排序麼?
小阿巴陷入沉思,想了半天,沒想出來。
于是我提示到:假如在一次考試中你想知道自己的排名,是不是隻需要知道有多少人的分數比自己高就行了,不用去管其他人排第幾對吧?
小阿巴一拍腦袋:對啊,我隻需要先查出自己的分數,然後統計分數大于我的使用者數量,不就知道自己的排名了?
先用 SQL 語句查出使用者的分數:
然後再用 SQL 語句統計分數大于該使用者分數的數量:
最後隻需要将該查詢結果加 1,就是自己的排名啦~
小阿巴感歎到:原來轉換一點點思路,就能省去多餘的排序帶來的性能開銷,起飛~
魚皮:先别起飛,其實對于一般使用者量的系統,上面的方案就已經足夠了。下面讓我們加大難度,假如使用者數再多一點點呢,比如說一億個,怎麼實時擷取前 10 名呢?
小阿巴:還真是 “億點點”,就您那破程式設計導航還想着有一億個使用者?
魚皮:少廢話,夢想還是要有的,萬一有億個使用者呢?快想想系統怎麼做!
小阿巴:且不說對一億個資料排序有多慢,能不能存的下都是個問題啊。。。啊,等等,這難道就是面試常見的 Top N 問題!
魚皮:不錯,我面試的時候被問過好幾次 Top N 問題,如何從海量資料中找出前 N 個數呢?
小阿巴:這我完全不懂啊,算法不會,真要命。
魚皮:其實 Top N 問題的核心在于保證空間和時間複雜度,先要考慮資料能存入記憶體運算,在怎樣算得更快。
通常 Top N 問題有下列幾種解決方案。
全部排序
直接對所有資料進行排序(快排等),缺點是需要将資料一次性加載到記憶體中。
局部淘汰
記憶體中維護一個大小為 N 的容器,再讓剩餘的數一個個進入容器,并淘汰容器内的最小值。最終容器内剩下的數就是前 N 名。優點是能節省記憶體,缺點是太慢了。
分治
把資料分為多個小組,小組内先分别選出前 N 名小組長,最後再讓這些小組長同台競技,選出最終的前 N 名。
哈希預處理
假如資料重複度很高,可以通過 hash 的方式,去掉很多重複資料。比如 1 億個資料裡,一半是 0,一半是 1,那麼取前 10 名時,可以直接淘汰掉另一半為 0 的資料。
但是預處理本身也需要時間和空間,這就需要我們對資料的重複度有一個清晰的判斷,否則自作聰明、适得其反。
小根堆
面試算法中的高頻考點 —— 堆排序,可以先取前 N 個數組成小根堆,堆頂始終是最小值。 然後周遊後續數字,大于堆頂就替換掉堆頂并調整最小堆結構。該算法時間複雜度和空間複雜度(為 N,常數)都不錯,是以必須要掌握。
但是具體選擇哪種方案呢?還是要結合我們實際的項目和業務場景來分析。
由于我們的資料庫來記錄積分,是以當使用者量級很大時,首先要 分庫分表 ,通常是水準分表,根據一定規則(比如 id)把使用者資料行分批存儲在多個資料表中。
然後就和大資料 Map / Reduce 處理機制一樣了,可以采用 分治 的方式 并行計算 每個表的前 10 名(map),都計算好後,再彙總到一起計算最終的前 10 名(reduce)。
用這種方式,别說 1 億了,2 億、3 億的計算模式都是一樣的,加機器水準擴容就好了~
是以遇到 Top N 問題的時候,大家可以先答一下上面的幾種方案,再結合具體的場景分析,分治和最小堆是我覺得相對 核心 的點。
最後,對于實時排行榜的設計,肯定很多背過八股文面試題的朋友在第一時間會想到使用 Redis 的有序集合 zset,的确也是一種方案,但也要結合場景去分析利弊,不要秒答。
使用基于記憶體的 Redis zset 的确運算更快,且天然支援排序、使用友善。但資料量大時同樣面臨資料更新、維護、同步、持久化存儲等問題,而且對于我們這種實時性要求不高的需求來說,有些大材小用了哈哈。
我是魚皮,肝文不易,點贊 還是要求一下的,祝大家都能心想事成、發大财、行大運。
最後再送大家一些 幫助我拿到大廠 offer 的學習資源 ,視訊教程 + 習題 + 答案 + 源碼、程式設計書籍、大廠面經、實戰項目等。
指路:跑了,留下 6T 的資源!
我是如何從零開始通過自學,拿到騰訊、位元組等大廠 offer 的,可以看這篇文章,不再迷茫!
指路:我學計算機的四年,共勉!