如何設計一個排行榜?

前言

大家好,我是田螺

最近有位星球粉絲問:田螺哥,如何設計一個排行榜?

日常開發中,我們經常需要涉及設計排行榜的需求,如禮物排行榜、微信運動排行、王者榮耀段位排行榜等等。今天我帶大家聊聊,排行榜如何設計。

數據庫的 order by

很多小夥伴,一提到排行榜,就想到數據庫的order by

其實這個思路是沒有錯的,我們假設用order by來實現一個簡單的微信運動步數排行榜。

假設表結構如下:

CREATE TABLE `user_info` (
   `id` int NOT NULL AUTO_INCREMENT,
   `user_name` varchar(255) DEFAULT NULL,
   `age` int DEFAULT NULL,
   `step` int DEFAULT 0 comment '步數',
   `picture_url` varchar(255) DEFAULT NULL comment '頭像url',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3

這時候要實現一個微信步數排行版,不是有手就行嘛:

select * 
from user_info
order by step desc

這個實現沒有問題的,如果表的數據量少的話,反而推薦這樣實現。如果數據量多呢?

我們先來造一千萬數據。

    for (int i = 0; i < 10000000; i++) {
            UserInfo record = new UserInfo();
            record.setUserName("user" + i);
            record.setAge(10);
            record.setUserName("userName" + i);
            record.setStep(new Random().nextInt(1000) * 1000);
            record.setPictureUrl("https://tianluo/picture/"+i+".jpg");
            userInfoDao.insertUserInfo(record);
        }

我們現在查詢步數前 1000 的用戶,SQL 如下:

select * 
from user_info
order by step desc limit 0,1000;

然後看下耗時,set @@profiling=1;,show profiles;

可以發現耗時 14 秒多, 這個耗時太久了,這種情況,我們可以添加索引優化它。

ALTER TABLE user_info ADD INDEX idx_step (step);

添加完索引之後,發現再去查步數前 1000 的用戶,0.005 秒:

這時候感覺,order by 做分頁也挺好的。但是這個是你加了索引和限制排序條數的前提下啦。

實際上,當數據量較小且查詢不頻繁時,可以使用 MySQL 的 order by 來實現排行榜。而當數據量較大且需要實時更新並頻繁查詢時,使用 Redis 的有序集合更爲適合。

Redis 的 zset

有序集合zsetRedis 提供的一種數據結構,它類似於集合(set),但每個成員都關聯着一個分數(score),Redis 使用這個分數來對集合中的成員進行排序。

大家可以看看官網對它的簡介哈:

https://redis.io/commands/zadd/#sorted-sets-101

我們繼續搞一個微信運動排行版。假設 redis 的 key 是 run:ranking, 然後小明的步數是 1000,小華的步數是 3000,小紅的步數是 2000,小李的步數是 4000 步,我們可以用 zadd 添加到redis,如下:

zadd run:ranking 1000 “小明”
zadd run:ranking 3000 “小華”
zadd run:ranking 2000 “小紅”
zadd run:ranking 4000 “小李”

我們用這個命令,就可以輸出排行榜啦:

ZREVRANGE run:ranking 0 -1 WITHSCORES

它在 Redis 類似這樣保存的:

如果在 redis 中的 score 分數相同,它是如何排序的呢?

在 Redis 的有序集合(Sorted Set)中,如果多個成員具有相同的分數,則它們按照字典順序進行排序。具體來說,當有多個成員具有相同的分數時,Redis 會根據成員的字典順序(lexicographical order)對它們進行排序。

字典順序是一種字符串比較的方式,它按照字母表的順序進行排序。對於 ASCII 字符集,字母表的順序是 A-Z,a-z,數字的順序是 0-9。如果成員具有相同的分數,那麼它們將按照它們的字典順序進行排列。

例如,假設有一個名爲 myset 的有序集合,包含了以下成員和分數:

"a" - 1000
"b" - 2000
"c" - 2000
"d" - 3000

在這個示例中,成員 "b" 和 "c" 具有相同的分數 2000。當使用 ZRANGE 或 ZREVRANGE 命令來查看有序集合的成員時,它們將按照字典順序進行排列。因此,成員 "b" 將在成員 "c" 的前面,因爲 "b" 的字典順序小於 "c"。

如果每個人看到的排行榜,都是一樣的,類似上面的處理就可以啦。如果每個人看到的排行榜不一樣的,我們則可以在redis zsetkey,加上用戶標誌,比如userId即可。

使用 Redis 的有序集合作爲排行榜,也有一些問題需要注意哈

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/SKm_xJvbKmCMtZNY9EvATg