面试场景题:如何设计一个排行榜?
这是一个常见的面试场景题,考验的是面试准备范围的广度,见过就会答。
思路
基于数据库
如果之前没有遇见过,最容易想到的就是平时接触的最多的数据库排序。前端每隔一段时间调用接口去查询数据库,然后更新排行榜表。
在一个用户量非常的小的具体场景,这是可行的。
如果是一个游戏排行榜的话,随着游戏玩家的增加,达到千万用户级别的话,虽然可以扯什么查询慢就加索引,数据量大就分库分表,但这仍然不是一个特别好的方案。
基于Redis
这个场景其实就是考察 Redis 中 sorted set 数据结构的掌握。
sorted set 是有序集合的意思。
例子:
- key 是 sport:ranking:20210227
- value 是一个集合,且这个集合是有序的
- 集合中的每一个 member 都有一个 score,然后按照这个 score 进行降序排序;
- member 是不可以重复的,但是 score 是可以重复的。
当 member 的 score 一样的时候,member 按照字典序(lexicographically) 进行排序,所以上图 jay 在 why 前。
有序集合的操作函数,一共有 32 个。
ZADD 方法
- 添加 member 命令格式:zadd key score member [score member ...]
- 增加 member 的 score 命令格式:zincrby key increment member
- 获取 member 排名命令格式:zrank/zrevrank key member
- 返回指定排名范围内的 member 命令格式:zrange/zrevrange key start end [withscores]
1、添加 member
zadd key score member [score member ...]
可以一次添加一对或者多对 score-member
zadd sport:ranking:20210227 10026 why
zadd sport:ranking:20210227 10158 mx 30169 les 48858 skr 66079 jay
执行之后,返回的数字代表添加成功的 member 个数。
2、增加 member 的 score
zincrby key increment member
微信运动排行榜的数据是实时更新的,执行命令:
zincrby sport:ranking:20210227 5000 why
按照 score 倒序:
3、获取 member 排名
# zrank 按照分数从低到高返回 member 排名
zrank key member
# zrevrank 按照分数从高到低返回 member 排名
zrevrank key member
现在要获取 jay 的排名,用 zrank 返回结果就是 4,用 zrevrank 时,jay 的排名就是 0:
zrank sport:ranking:20210227 jay
zrevrank sport:ranking:20210227 jay
4、返回指定排名范围内的 member
member:zrange/zrevrange key start end [withscores]
zrange 是按照 score 从低到高返回指定排名范围内的 member。
zrevrange 是按照 score 从高到低返回指定排名范围内的 member。
用 zrevrange 的命令获取步数排名前三的 member:
zrevrange sport:ranking:20210227 0 2
带上可选参数 withscores 之后,会返回对应 member 的 score:
要获取所有用户的排名:
zrevrange sport:ranking:20210227 0 -1
微信步数排行榜
每个人看见的数据排行数据来源自己的微信好友,而微信好友各不相同,所以看到的排行榜也各不相同。
当前的 key 是 sport:ranking:20210227,里面只包含了某一天的信息,只要在 key 里面加上用户的属性就可以了。
例如 key 可以设计为 sport:ranking:why:20210227 ,由于 key 里面多了用户信息,每个人的 key 都各不相同。
亿级用户排行榜
需要利用分治的思想。
如王者一共 8 个段位,可以有 8 个,这个桶可以是一个 Redis 里面的 8 个不同的 key,甚至是 8 个 Redis 里面各一个 key:
对于亿级用户只分 8 个桶未免太少了,那就根据每个段位里面还有小段位继续分桶。
还是不够的话,直接把段位加上各种其他条件换算成积分,然后按照积分来拆分:
当然实际情况下,用户的落点其实并不是均匀的,这就需要进行数据分析,通过一系列的高数、概率、离散等知识去做个桶大小的预估。