Skip to content

面试速答(先看这里)

**一句话结论:**zset可以实现,将每个用户的得分作为zset中元素的score,将用户ID作为元素的value。

60秒标准回答:

在Redis中,使用zset可以实现排行榜的功能这个大家都知道

zset可以实现,将每个用户的得分作为zset中元素的score,将用户ID作为元素的value。使用zset提供的排序功能,可以按照分数从高到低排序, 但是如果分数相同,按照默认的排序规则会按照value值排序 ,而不是按照时间顺序排序

为了实现分数相同按照时间顺序排序, 我们可以将分数score设置为一个浮点数,其中整数部分为得分,小数部分为时间戳 ,如下所示

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**在Redis中,使用zset可以实现排行榜的功能这个大家都知道。
  • **要点2:**为了实现分数相同按照时间顺序排序, 我们可以将分数score设置为一个浮点数,其中整数部分为得分,小数部分为时间戳 ,如下所示:
  • **要点3:**假设现在的时间戳是1680417299000,除以1e13得到0.1680417299000,再加上一个固定的分数(比如10),那么最终的分数就是10.1680417299000,可以将它作为zset中某个成员的分数,用来排序。
  • **要点4:**实现了分数倒序排列,分数相同时间戳大的排在了前面,这和我们的需求相反了,所以,就需要在做一次转换。
  • **要点5:**这样可以保证分数相同时,按照时间戳从小到大排序,即先得分的先被排在前面。

**记忆锚点:**zset → e13 → score → Redis → value

加分表达:

  • 使用zset提供的排序功能,可以按照分数从高到低排序, 但是如果分数相同,按照默认的排序规则会按照value值排序 ,而不是按照时间顺序排序。

追问准备:

  • 围绕「zset」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「e13」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「score」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

在Redis中,使用zset可以实现排行榜的功能这个大家都知道。

zset可以实现,将每个用户的得分作为zset中元素的score,将用户ID作为元素的value。使用zset提供的排序功能,可以按照分数从高到低排序,但是如果分数相同,按照默认的排序规则会按照value值排序,而不是按照时间顺序排序。

为了实现分数相同按照时间顺序排序,我们可以将分数score设置为一个浮点数,其中整数部分为得分,小数部分为时间戳,如下所示:

> score = 分数 + 时间戳/1e13

假设现在的时间戳是1680417299000,除以1e13得到0.1680417299000,再加上一个固定的分数(比如10),那么最终的分数就是10.1680417299000,可以将它作为zset中某个成员的分数,用来排序。

这么做了之后,假如有四个数字:

10.1680417299000、10.1680417299011、11.1680417299000、11.1680417299011

他们按照倒序拍完顺序之后,会是:

11.1680417299011>11.1680417299000>10.1680417299011>10.1680417299000

实现了分数倒序排列,分数相同时间戳大的排在了前面,这和我们的需求相反了,所以,就需要在做一次转换。

> score = 分数 + 1-时间戳/1e13 > > 因为时间戳是这种形式1708746590000 ,共有13位,而1e13是10000000000000,即1后面13个0,所以用时间戳/1e13就能得到一个小数

这样可以保证分数相同时,按照时间戳从小到大排序,即先得分的先被排在前面。

代码实现如下:

plain
import redis.clients.jedis.Jedis;
/**
*@author Hollis
**/
public class RedisZsetDemo {
    private static final String ZSET_KEY = "my_zset";

    public static void addMember(String member,int score, long timestamp, Jedis jedis) {
        double final_score = score + 1 - timestamp / 1e13;
        jedis.zadd(ZSET_KEY, final_score, member);
    }
}