面试场景题:如何设计一个排行榜?

这是一个常见的面试场景题,考验的是面试准备范围的广度,见过就会答。

思路

基于数据库

如果之前没有遇见过,最容易想到的就是平时接触的最多的数据库排序。前端每隔一段时间调用接口去查询数据库,然后更新排行榜表。

在一个用户量非常的小的具体场景,这是可行的。

如果是一个游戏排行榜的话,随着游戏玩家的增加,达到千万用户级别的话,虽然可以扯什么查询慢就加索引,数据量大就分库分表,但这仍然不是一个特别好的方案。

基于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 个桶未免太少了,那就根据每个段位里面还有小段位继续分桶。

还是不够的话,直接把段位加上各种其他条件换算成积分,然后按照积分来拆分:

当然实际情况下,用户的落点其实并不是均匀的,这就需要进行数据分析,通过一系列的高数、概率、离散等知识去做个桶大小的预估。

posted @ 2022-02-26 22:03  当康  阅读(1261)  评论(0编辑  收藏  举报