主题
面试速答(先看这里)
**一句话结论:**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);
}
}