凉了呀,面试官叫我设计一个排行榜。 (上)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 凉了呀,面试官叫我设计一个排行榜。 (上)

这是why哥的第89篇原创文章

前两天,有一个读者给我发了一张图片。

我问:发什么肾么事了?

于是有了这样的对话:


微信图片_20220427220623.png


他发的图,就是微信运动步数排行榜的截图:


image.png


其实扯了这么多,这就是个常见的面试场景题:如何设计一个排行榜?

这个题吧,其实就是考你面试准备范围的广度,见过就会答,没见过...就难说了。

当然,如果你在实际业务中做过排行榜,那么这题正中下怀,你也不要笑出声来,场景题面试官是会给你思考时间的。


image.png


所以你不要张口就来,你只需要眉头稍稍一皱,给面试官说:这题我想想啊。

然后稍微组织一下语言,说出来就行。

这次的文章,就带着大家分析一下“排行榜”这个场景题,到底应该怎么做。


基于数据库


这个题,如果是真的之前没有遇见过,可能最容易进入大家视野的就是平时接触的最多的数据库了。

因为一想到“排行榜”,就想到了 order by。

一想了 order by,就想到了数据库。

一想到了数据库...

兄弟,你路就走窄了。

虽然我曾经就基于 MySQL 做过排行榜,因为当时是为了一个比赛临时搭建的服务,根本就没有引入 Redis。我评估了一下搭建 Redis 的时间和用 MySQL 直接开发的时间。

于是选择了 MySQL。

而让我选择 MySQL 的根本原因还是我已经知道进入决赛的队伍只有 10 支,也就是说我的排行榜表里面从始至终也只有 10 条数据。

选手提交代码之后,系统实时算分,然后更新排行榜表。

然后接口返回给前端页面下面这些数据,而下面这些数据都在一个表里面:

  • 队伍按照历史最高分数排名
  • 队伍名称
  • 历史最高分数
  • 最近一次提交得分
  • 最近一次提交时间

前端每隔一分钟调用我的接口,相同分数,名次相同,所以我在接口里面用一条比较复杂的 sql 去查询数据库,上面的这些字段就都有了。

你看,排行榜确实是可以用 MySQL 来做的。

不一定非得上 Redis,记住一句话:脱离业务场景的方案设计,都是耍流氓。


image.png


但是这玩意和“万物皆对象”一样,别对着面试官说,这一定不是面试官想要听到的答案。

或者说,这只是想要听到的一部分回答。

这个回答能用的原因是我给了一个具体的场景,用户量非常的小,怎么玩都可以。

甚至我们不借助 MySQL 的排序,把数据查出来,在内存里面排序都可以。

但是如果,这是一个游戏排行榜,随着游戏玩家的增加,达到千万用户级别的话,这个方案肯定是不行了。

当然,也许你会给我扯什么查询慢就加索引,数据量大就分库分表的方案。

怎么说呢,上面这句话是没有错的。

但是一旦数据量大起来了,这个方案其实就不是一个特别好的方案。

这问题,得从根上治理。


基于 Redis


这个场景其实就是在考察你对于 Redis 的 sorted set 数据结构的掌握。

sorted set,见名知意,就是有序集合的意思。


image.png

前面的 sport:ranking:20210227 是 Redis 中的 key。

value 是一个集合,且可以看出这个集合是有序的。集合中的每一个 member 都有一个 score,然后按照这个 score 进行降序排序。

需要注意的是,图片中的 score/member 不是我随便写的,官网上就是这样定义的:

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

在 Redis 中它大概是长这样的:


image.png


而且官网上说的是: score / member pairs。

所以我画图的时候,score 在前,member 在后。这可不是随便画的,虽然谁前谁后好像也不影响什么玩意。

另一个需要注意的点是,虽然我的示意图中没有体现出来,但是在有序集合中,元素即 member 是不可以重复的,但是 score 是可以重复的。

这个很好理解,就比如 20210227 这一天的微信步数,我可以走 6666 步,你也可以走 6666 步,这个是不冲突:


image.png


但是,问题就随之而来了:当 member 的 score 一样的时候,member 是怎么排序的呢?

看一下来自官网的答案:

image.png


当多个元素具有相同的分数时,它们按照 lexicographically 进行排序。

哎呀,lexicographically 这个单词不认识。

不慌,你知道的 why哥还兼职教英文:


image.png


当分数一样的时候,按照字典序排序,所以上面的示意图 jay 在 why 之前。

接下来,看一下有序集合的操作函数,一共有 32 个:


微信图片_20220427220912.png


我这里就不一个个的做 API 教学了,官网上已经写的很清楚了,如果对于不熟悉的命令,可以去官网上查看,都是有示例代码的。

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

比如这个 ZADD 方法:


image.png


目录
相关文章
|
缓存 网络协议 网络性能优化
UDP实现可靠传输
UDP实现可靠传输
|
算法 Linux C++
【Linux系统编程】深入解析Linux中read函数的错误场景
【Linux系统编程】深入解析Linux中read函数的错误场景
484 0
|
缓存 Java 应用服务中间件
面试官:如何实现多级缓存?
面试官:如何实现多级缓存?
465 1
|
API 网络安全 网络架构
浅谈Elastic Search V8版本的一些重大改进
浅谈Elastic Search V8版本的一些重大改进
403 0
|
存储 前端开发 JavaScript
State 状态管理最佳实践
【10月更文挑战第1天】本文深入浅出地介绍了前端开发中的状态管理概念,强调其在构建复杂单页应用(SPA)中的重要性。文章详细阐述了状态管理的核心原则,如单一源真理、状态不可直接修改及状态变更透明,并对比分析了如Redux、Vuex和MobX等常用状态管理库。通过具体代码示例,指出了状态分散和非原子操作等常见问题及其解决方案,为开发者提供了实用指导。
516 2
|
机器学习/深度学习 编解码 算法
【前沿解读】17篇2023淘天业务技术A类顶会论文(上)
【前沿解读】17篇2023淘天业务技术A类顶会论文(上)
490 2
|
设计模式 Java 微服务
你一定要知道业务开发最常用的两种设计模式
文章介绍了业务开发中最常用的两种设计模式:策略模式和异步形式的责任链模式,通过具体案例展示了它们在代码解耦、扩展性增强以及提升响应速度方面的应用,并强调了设计模式在提升代码质量和开发效率中的重要性。
|
存储 关系型数据库 MySQL
MySQL的MyISAM引擎:技术特点与应用场景
【4月更文挑战第20天】MySQL的MyISAM引擎特点是表级锁定,适合读多写少的场景,不支持事务但提供全文索引,适用于只读应用、全文搜索和简单备份恢复。在选择存储引擎时,应根据具体需求权衡。
1192 11
|
存储 安全 编译器
【C++ 多态 】深入理解C++的运行时类型信息(RTTI):dynamic_cast和typeid的应用与原理
【C++ 多态 】深入理解C++的运行时类型信息(RTTI):dynamic_cast和typeid的应用与原理
1216 1
|
缓存 前端开发 Java
Spring Boot中防止接口重复提交
Spring Boot中防止接口重复提交
1705 0