使用Redis实现积分排行榜,并支持同积分按时间排序

2023-05-16

排行榜这个功能很常见,多用于激励用户活跃和拉新,比如CSDN平台实现的周榜,按照每周文章总阅读量进行排名,用排名和奖品激励用户持续在平台上输出高质量内容。

最近笔者也做了一个积分排行榜的功能,在某些场景下我们需要处理同分排名问题。

如张三和李四、王五等人的积分都是100,我们需要实现按最先达到100积分的顺序对他们进行排名,也就是需要按时间排名。

使用Redis实现实时更新的排行榜并不难,Redis提供的ZSet数据结构就很适合用于实现排行榜,但如何实现相同积分情况下再支持按时间排序呢?
实现思路

可能是在此之前笔者刚实现分布式ID生成Base Service,下意识想到了分布式ID雪花算法的原理,即用一个long类型变量存储多个信息。一个long类型长度为8个字节(64bit),雪花算法使用其中41bit记录时间戳,其余bit位存储机房id、机器id、序列号。

Redis的ZSet支持分值为double类型,也是8字节,那么我们也可以使用41位存储时间戳,其实位存储用户的实际积分。

在雪花算法中最高位是不用的,目的是不允许生成负数ID,而在实现排行榜中没有这个限制,因为我们最终要的只是用户的积分,而不是加上时间戳的分值。但也要求最高位要么全为0,要么全为1,避免排序错乱。如实现积分倒序排名时可设置最高位全为1,只不过ZSet已经支持倒序获取,不需要多此一举,所以最高位我们依然不使用。

除去最高位和存储时间戳的41位后,剩余22位表示积分,这时我们还需要结合业务考虑,如果觉得22bit不够表示积分,那么还可以继续压缩时间戳占用的bit。

由于排行榜是周期性的,如周榜、月榜,所以我们没必要存储完整的时间戳,可以取当前时间与周期开始时间相差的毫秒数,这样就可以将41bit压缩到32bit、16bit、或者更低,具体需要多少个bit留给看官们自己算啦。

如果是用41bit表示时间戳,22bit表示积分的话,那么score的组成就是这样的:
0(最高位不用)|0000000 00000000 0000000(22bit表示积分)|0
00000000 00000000 00000000 00000000 00000000(41bit表示时间戳)

因为排序首先按积分排再按时间排,所以积分在高位,时间戳在低位,这样不管时间戳的值是多少,积分越大,64bit表示的数值就越大。

不过这样还没完。

在积分相同的情况下,是不是时间戳越大64bit表示的数值就越大?而我们需要的是按时间升序排,也就是最先达到xx积分的用户排在最前面,所以我们不能单纯的使用41bit存储时间戳,而应该是存储一个随时间流逝而变小的数值。

由于排行榜都会有一个周期,如周榜是一周,月榜是一个月,所以我们使用41bit存储的是一个周期的结束时间yyy-MM-dd 23:59:59对应的时间戳与用户积分更新时间的时间戳的差值,这个值会随着时间的推移而变小,而且不会出现负数的情况,刚好能够达到目的。
实现关键代码

1.实现积分+时间戳差值转score

// periodEndTimestamp: 当前周期结束时间的时间戳 
// 需确保point不会超过22bit所能表示的数值:2097151
private static long toScore(int point, long periodEndTimestamp) {
    long score = 0L;
    score = (score | point) << 41;
    score = score | (periodEndTimestamp - TimestampUtils.currentTimeMillis());
    return score;
}

2.实现从score中获取积分

private static int getPoint(long score) {
     return (int) (score >> 41);
}

3.更新积分

@Override
public void updateRanking(Integer periodId, Long accountId, Integer addPoint) {
    String key = String.format(RankingCacheKeys.REALTIME_POINT_RANKING_KEY, periodId);
    Double score = redisTemplate.opsForZSet().score(key, String.valueOf(accountId));
    score = (score == null) ? 0d : score;
    int curPoint = getPoint(score.longValue());
    long newScore = toScore(curPoint + addPoint, getCurPeriodEndDateTimestamp(periodId));
    redisTemplate.opsForZSet().add(key, String.valueOf(accountId), newScore);
}

总结

基于Redis ZSet实现积分排行榜(倒序)并支持按时间(升序)排序原理与注意事项:

1.将分值score的8字节拆分使用,最高位不用,其余一部分存储实际分值,一部分存储时间戳;

2.先按积分排序,再按时间排序,所以需要高位存储积分,低位存储时间戳,这样才能保证积分越高对应score越大;

3.同分值情况下按时间升序排序,必然让达到当前积分时间最早的score越大;

4.避免积分或者时间戳溢出,如8bit最大可以表示255,如果积分最大可以超过255,那么就需要考虑给积分加到9位…

5.由于每次更新用户积分都需要重新计算score,不能使用ZSet的原子性操作命令,因此可能存在并发数据一致性问题,这点需要考虑。

留一个思考题:如果再加一个条件排序,你觉得能实现吗?
关于实现排行榜用到ZSet的几个命令

1.获取倒序排名:

ZREVRANGEBYSCORE key min max offset count

2.获取某个用户的score:

ZSCORE key member

3.获取参与排名的总用户数(本期参与人数):

ZCARD key

4.获取某个用户的当前排名(倒序排序,从大到小的排名):

ZREVRANK key member
————————————————
版权声明:本文为CSDN博主「Java艺术」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/baidu_28523317/article/details/117829202

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用Redis实现积分排行榜,并支持同积分按时间排序 的相关文章

  • Linux常用终端命令之cat、grep、echo

    这三个指令 xff0c 每一个都很常用 xff0c 用法也都很多 作为一个linux初学者 xff0c 我还不能很好的掌握三个命令的用法 xff0c 于是先在这篇博客里做一个简单的整理和总结 xff0c 以加深对三个指令的理解 grep 先
  • Python+Pycharm使用opencv

    http blog csdn net whykifan article details 66478421 在这个配置过程中 xff0c 遇到了不少的问题 xff0c 于是就写了这篇博文 xff0c 希望可以帮到遇到相同问题的人 主要步骤 x
  • Linux启动流程rcN.d rcS.d rc.local等

    1 环境 当前系统环境为 xff1a Linux mint mate 17 1 基于ubuntu14 04的衍生版 备注 xff1a etc rc d文件夹中的脚本文件的链接目标为 xff1a etc init d文件夹下的脚本 为系统运行
  • Ubuntu 启用root账户并开启远程登录

    生产环境尽量不要如下操作 生产环境尽量不要如下操作 生产环境尽量不要如下操作 本地虚拟机 xff0c 为了方便 xff0c 直接root远程登录 Ubuntu 20 4 1 启用root账户 sudo passwd root 2 开启远程登
  • datatable中报错 table.XXX is not a function的解决方法

    在datatable中可以通过三种不同的方式为一个或多个表获取一个新的Datatables API实例 xff1a xff08 1 xff09 selector DataTable xff08 2 xff09 selector dataTa
  • 在ubuntu下安装libpcap库

    这两天公司里要我了解一下pcap xff0c 但是还不知道它是干什么的 首先 xff0c 我从网上查到了 xff0c pcap实际上是抓包库 这个抓包库给抓包系统提供了一个高层次的接口 所有网络上的数据包 xff0c 甚至是那些发送给其他主
  • Python 获取当前路径(文件及所在文件夹,正斜线)

    参考博客 xff1a http www cnblogs com wind wang p 5822192 html 更多路径读取请参照上述博客 xff08 使用Python 2 x版本 xff09 xff0c 这里只挑出个人认为最直接 常用的
  • 基于docker搭建mysql,redis,mongo,gitlab,wiki等开发环境

    今天基于docker搭建一整套开发环境 首先安装docker yum y install docker io 使用加速器 xff1a vim etc docker daemon json 添加163的加速地址 xff1a 34 regist
  • Android编译系统分析五:system.img的生成过程

    Android编译系统分析系列文章 xff1a android编译系统分析 xff08 一 xff09 source build envsetup sh与lunch Android编译系统 xff08 二 xff09 mm编译单个模块 an
  • linux学习笔记--Xshell远程登陆linux

    1 登录xshell官网XSHELL NetSarang Website xff0c 输入姓名和邮箱 xff0c 下载免费版的Xshell xff0c 下载链接会发送到邮箱中 下载完成后直接安装即可 2 双击打开安装完成的Xshell xf
  • Apache使用localhost可以访问但使用本机IP(局域网)不能访问

    Apache使用localhost可以访问但使用本机IP 局域网 不能访问 Apache 使用localhost 127 0 0 1 可以访问 xff0c 使用本机IP 局域网 不能访问 xff0c 为什么本机ip地址不能访问localho
  • ubuntu14.04LTS命令行安装xfce4桌面

    安装ubuntu14 04LTS后 xff0c 需要一个桌面 xff0c 因此选择安装xfce4 1 安装 1 1 安装默认版本4 10 sudo apt get install xfce4 1 2 安装版本4 12 1 sudo add
  • OCLint的部分规则(Convention 部分)

    OCLint的部分规则 xff08 Convention 部分 xff09 对OCLint的部分规则进行简单翻译解释 xff0c 有部分进行了验证以及进一步分析 测试 OCLint其他相关内容如下 xff1a OCLint iOS OC项目
  • 修改virualbox下的虚拟系统Ubuntu的内存

    VBoxManage exe在VirtualBox 安装目录下 xff0c 如下图 xff0c 我们进VirtualBox 安装目录查看到VBoxManage exe 要使用这个工具 xff0c 就先了解一下这个工具吧 xff0c 要用命令
  • 四旋翼定高篇之惯导加速度+速度+位置三阶互补融合方案

    笔者最近正在做四旋翼惯性导航的部分 xff0c 利用加速度计进行速度估计 位置估计 xff0c 从而实现四旋翼的垂直方向上的定高 水平方向上的定点控制 首先在这里引用学长之前参考APM飞控里面互补滤波惯导融合方案 xff1a 原文见四旋翼位
  • 从APM源码分析GPS、气压计惯导融合

    最近事多 xff0c 忙着开源自研飞控 xff0c 现主要工作基本已经完成 xff0c 代码最迟下月中旬开放 xff0c 博客来不及更新 xff0c 还请各位见谅 xff0c 后面会抽空多更的咯 xff01 xff01 xff01 自研飞控
  • Angular 展示标签内容,而不是标签本身

    在angualr项目的html页面中 xff0c 如果展示的数据包含html标签 xff0c 则不会被解析 xff0c 而是原样展示标签 xff0c 比如 lt br gt xff0c 而不会产生换行的效果 xff0c 解决办法是 xff0
  • 安装ROS过程中的rosdep init 和 rosdep update 命令执行不成功的解决办法

    一 解决 rosdep init 命令执行不成功 xff1a 不成功信息 xff1a RROR cannot download default sources list from https raw githubusercontent co
  • ubuntu如何开启22端口支持ssh访问

    1 查看本机IP ifconfig 执行后如果提示服务不存在 xff0c 则需要下载该工具 sudo apt install net tools 2 查看端口22是否被占用 netstat nltp grep 22 n 不以进程的服务名称
  • 算法 - 剑指Offer 二叉搜索树的第k大节点

    题目 给定一棵二叉搜索树 xff0c 请找出其中第 k 大的节点的值 解题思路 这题比较简单 xff0c 首先题目中说需要倒数第几大的数字 xff0c 第一反应就是这个坑定是一个倒序数组 xff0c 然后去找 xff0c 然后我们可以知道的

随机推荐

  • 解决Unable to add window -- token android.os.BinderProxy is not valid; is your activity running?

    运行项目有时候在dialog这里一直报错 xff0c 按照日志在网上找解决方案 很多都跑到了底层去解决这问题 然而我不懂底层 xff0c 没办法 继续找咯 苍天明鉴 xff01 找到问题了 原因一般是展示dialog的时候用的是异步 xff
  • 程序员生涯之生活篇

    作为一名非正规的程序员菜鸟 xff0c 我没有什么职场沉淀 xff0c 也没有开阔的视野 xff0c 只是读了几篇博文与时讯 xff0c 有所感触 xff0c 想要记录一下自己的想法与体会 xff0c 暂定为程序员生涯之生活篇 首先介绍一下
  • 【Kubernetes系列】私有仓库Harbor和Registry的安装使用

    目录 一 Harbor 简介二 Registry 简介三 Harbor 和 Registry 比较四 Harbor 的安装使用1 环境要求 xff08 1 xff09 硬件要求 xff08 2 xff09 软件要求 2 环境准备 xff08
  • Ping过程 原理 详解

    如果你想了解PING的原理 xff0c 就看我的文章 xff0c 不要去网上找 xff0c 找不到什么好的内容 看了我文章 xff0c 也许你会从对网络一窍不通 xff0c 到豁然开朗 先看拓朴图 xff1a 我在这里讲拼的两情况 一种是同
  • java怎么去除字符串中的数字。。。

    比如 xff1a str 61 123assume345contribute 把数字去掉 str 61 assumecontribute public class Hello public static void main String a
  • FFplay源码分析-EOF

    本系列 以 ffmpeg4 2 源码为准 xff0c 下载地址 xff1a 链接 xff1a 百度网盘 提取码 xff1a g3k8 FFplay 源码分析系列以一条简单的命令开始 xff0c ffplay i a mp4 a mp4下载链
  • ubuntu离线安装软件及依赖

    因为工作站不能联网 xff0c 所以需要离线下载相应的安装包来安装 Linux中的安装包有个问题 xff1a 一个包可能有很多个依赖 在联网条件下 xff0c 会直接下载相应的依赖 xff0c 但是在离线条件下 xff0c 如何确保依赖下载
  • Ubuntu16.04 获取并启用root账户的方法

    1 打开终端 xff0c 输入 xff1a sudo passwd root xff0c 然后更改root密码 2 输入 xff1a su root xff0c 是否可以进入root用户 xff0c 如果出现前面root 64 用户名 xf
  • AngularJs与ReactJS优缺点&适用场景

    Angular的优缺点 xff1a 优点 AngularJS是一套完整的框架 xff0c angular有自带的数据绑定 render渲染 angularUI库 过滤器 directive 模板 服务 q defer http xff0c
  • Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'inform

    Expression 1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 39 information schema PROFIL
  • rabbitmq的消息持久化处理开启,再关闭后,消费者启动报错

    今天在测试rabbitmq的消息持久化处理时 xff0c 一切顺利 xff0c 可是再想测试ACK消息确认机制时 xff0c 消费者却无法启动了 xff0c 报如下错误 xff1a org springframework amqp rabb
  • VMware下Linux无法播放声音的解决方案

    问题背景 宿主机环境 xff1a Windows 10 VMware Workstation版本 xff1a VMware Workstation 12 VMware Workstation 15 客户机环境 xff1a CentOS 7
  • pip更新及Requirement already up-to-date解决方法

    pip更新及Requirement already up to date解决方法 文 xff1a 铁乐与猫 2018 9 11 更新命令 将pip更新到最新版本 python m pip install upgrade pip Anacon
  • 如何高效的使用适配器Adapter

    如何高效的使用适配器Adapter 当我们在使用ListView GridView时 xff0c 都会给其设置相应的适配器 xff0c 用来给其设置数据 xff0c 会去继承BaseAdapter xff0c 重写getCount getI
  • kotlin 没有@Parcelable注解

    在使用kotlin的时候 xff0c 有的时候需要对实体类进行序列化的操作 序列化的方式就两种 xff0c 一种是Serializable xff0c 一种是Parcelable 在android中 xff0c 基本都是使用的Parcela
  • Android studio3.0+ 编译Lame库(CMake方式)

    最近在学习音视频方面的知识 xff0c 购买了音视频开发进阶指南 xff0c 在交叉编译LAME库的时候 xff0c 书中使用的还是旧版本的编译方式 xff0c 现在android studio在2 2以后就开始使用CMake的编译方式了
  • 打印正整数n拆分的所有情况

    题目 xff1a 把一个正整数n拆分成若干个正整数的相加 xff0c 列出所有组合 例如 xff1a 4 61 4 4 61 1 43 3 61 2 43 2 4 61 1 43 1 43 2 4 61 1 43 1 43 1 43 1 动
  • redis实现积分排行榜

    在项目开发中常常遇到一些积分排行的问题 一个典型的积分行榜包括以下常见功能 xff1a 能够记录每个用户的分数 xff1b 能够对用户的分数进行更新 xff1b 能够查询每个用户的分数和名次 xff1b 能够按名次查询排名前N名的用户 xf
  • 如何减小Ubuntu 16.04系统下VMware虚拟机硬盘空间占用过大问题

    VMware虚拟机占用硬盘空间只增大不减少 xff0c 即使你删除文件 xff0c 占用的硬盘空间也不释放 用了一段时间后空间不够了 解决办法 方法一 在vmware的安装目录下 xff0c 有一个vmware vdiskmanager 关
  • 使用Redis实现积分排行榜,并支持同积分按时间排序

    排行榜这个功能很常见 xff0c 多用于激励用户活跃和拉新 xff0c 比如CSDN平台实现的周榜 xff0c 按照每周文章总阅读量进行排名 xff0c 用排名和奖品激励用户持续在平台上输出高质量内容 最近笔者也做了一个积分排行榜的功能 x