redis实现积分排行榜

2023-05-16

在项目开发中常常遇到一些积分排行的问题。
一个典型的积分行榜包括以下常见功能:

  1. 能够记录每个用户的分数;
  2. 能够对用户的分数进行更新;
  3. 能够查询每个用户的分数和名次;
  4. 能够按名次查询排名前N名的用户;
  5. 能够查询排在指定用户前后M名的用户;

因为排行榜的实时性,所以这个需要在第一时间进行查询并展示。由于一个用户的名次上升x位将会引起x+1位用户的名次发生变化(包括该用户),如果采用传统数据库(比如MySQL)来实现排行榜,当用户人数较多时,将会导致对数据库的频繁修改,从而降低数据库的性能。所以只能另辟蹊径,来解决排名的问题。

redis作为NoSQL中的一员,近年来得到广泛应用,Redis拥有更多的数据类型和操作接口,具有更大的适用范围,其中的有序集合(sorted set,也称为zset)就非常适合于排行榜的构建。

有序集合首先是集合,其成员(member)具有唯一性,其次,每个成员关联了一个分数(score),使得成员可以按照分数排序。
关于有序集合的介绍见http://redis.io/topics/data-types#sorted-sets,
其命令见http://redis.io/commands#sorted_set。

下面介绍几个能用于排行榜的命令。

假设TeamRank为排行榜名称,user1、user2等为用户唯一标识(现实中可能是用户 ID)。

1) zadd——设置用户分数

  • 命令格式:zadd 排行榜名称 分数 用户标识 时间复杂度:O(log(N))
设置4个用户的分数,如果用户分数已经存在,则会覆盖之前的分数
127.0.0.1:6379> zadd TeamRank 89 user1
(integer) 1
127.0.0.1:6379> zadd TeamRank 95 user2
(integer) 1
127.0.0.1:6379> zadd TeamRank 95 user3
(integer) 1
127.0.0.1:6379> zadd TeamRank 90 user4
(integer) 1

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2) zscore——查看用户分数

  • 命令格式:zscore 排行榜名称 用户标识 时间复杂度:O(1)
	查看user2这个用户在TeamRank排行榜中的分数
	127.0.0.1:6379> zscore TeamRank user3
	"95"

  
  
  
  
  • 1
  • 2
  • 3

3) zrevrange——按名次查看排行榜

  • 命令格式:zrevrange 排行榜名称 起始位置 结束位置 [withscores] 时间复杂度:O(log(N)+M)

  • 由于排行榜一般是按照分数由高到低排序的,所以我们使用zrevrange, 而命令zrange是按照分数由低到高排序。

  • 起始位置和结束位置都是以0开始的索引,且都包含在内。如果结束位置为 -1则查看范围为整个排行榜。

  • 带上withscores则会返回用户分数。

	查看所有用户分数
	127.0.0.1:6379> zrevrange TeamRank 0 -1 withscores 
	1) "user3"
	2) "95"
	3) "user2"
	4) "95"
	5) "user4"
	6) "90"
	7) "user1"
	8) "89"

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
查询前三名用户分数。
127.0.0.1:6379> zrevrange TeamRank 0 2 withscores 
1) "user3"
2) "95"
3) "user2"
4) "95"
5) "user4"
6) "90"

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4) zrevrank——查看用户的排名

  • 命令格式:zrevrank 排行榜名称 用户标识 时间复杂度:O(log(N))

  • 与zrevrange类似,zrevrank是以分数由高到低的排序返回用户排名(实际返回的是以0开始的索引),对应的zrank则是以分数由低到高的排序返回排名。

	查询用户user3和user4的排名。
	127.0.0.1:6379> zrevrank TeamRank user3
	(integer) 0
	127.0.0.1:6379> zrevrank TeamRank user4
	(integer) 2

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5

5) zincrby——增减用户分数

  • 命令格式:zincrby 排行榜名称 分数增量 用户标识 时间复杂度:O(log(N))

  • 有的排行榜是在变更时重新设置用户的分数,而还有的排行榜则是以增量方式修改用户分数,增量可正可负。如果执行zincrby时用户尚不在排行榜中,则认为其原始分数为0,相当于执行zdd。

	将user4的分数增加6,使其名次上升到第一位。
	127.0.0.1:6379> zincrby TeamRank 6 user4
	"96"
	127.0.0.1:6379> zrevrank TeamRank user4
	(integer) 0

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5

6) zrem——移除某个用户

  • 命令格式:zrem 排行榜名称 用户标识
    时间复杂度:O(log(N))
	移除用户4
	127.0.0.1:6379> zrem TeamRank user4
	(integer) 1
	127.0.0.1:6379> zrevrange TeamRank 0 -1 withscores
	1) "user3"
	2) "95"
	3) "user2"
	4) "95"
	5) "user1"
	6) "89"

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

7) del——删除排行榜

  • 命令格式:del 排行榜名称

  • 排行榜对象在我们首次调用zadd或zincrby时被创建,当我们要删除它时,调用redis通用的命令del即可。

	删除 TeamRank 排行榜
	127.0.0.1:6379> del TeamRank 
	(integer) 1
	127.0.0.1:6379> zrevrange TeamRank 0 -1 withscores
	(empty array)	

  
  
  
  
  • 1
  • 2
  • 3
  • 4
  • 5

分数相同的排行处理

  • 免费的方案总有那么一些不完美。从前面的例子我们可以看到,user2和user3具有相同的分数,但在按分数逆序排序时,user3排在了user2前面。而在实际应用场景中,我们更希望看到user2排在user3前面,因为user2比user3先加入排行榜,也就是说user2先到达该分数。但Redis在遇到分数相同时是按照集合成员自身的字典顺序来排序,这里即是按照”user2″和”user3″这两个字符串进行排序现(现实中可能是用户 ID),以逆序排序的话user3自然排到了前面。

  • 要解决这个问题,我们可以考虑在分数中加入时间戳,

  • 计算公式为:带时间戳的分数 =实际分数*10000000000+(9999999999 –timestamp)

  • timestamp我们采用系统提供的time()函数,也就是1970年1月1日以来的秒数,我们采用32位的时间戳(这能坚持到2038年),由于32位时间戳是10位十进制整数(最大值4294967295),所以我们让时间戳占据低10位(十进制整数),实际分数则扩大10^10倍,然后把两部分相加的结果作为zset的分数。考虑到要按时间倒序排列,所以时间戳这部分需要颠倒一下,这便是用9999999999减去时间戳的原因。当我们要读取用户实际分数时,只需去掉后10位即可。

  • 这里有个大问题,因为Redis的分数类型采用的是double,64位双精度浮点数只有52位有效数字,它能精确表达的整数范围为-2^53 到 2^53,最高只能表示16位十进制整数(最大值为9007199254740992,其实连16位也不能完整表示)。这就是说,如果前面时间戳占了10位的话,分数就只剩下6位了,这对于某些排行榜分数来说是不够用的。我们可以考虑缩减时间戳位数,比如从2019年1月1日开始计时,推荐采用
    7位+8位 的的模式来进行排行 99999999 秒=1157.40739583
    天也就是可以存储三年左右的数据,而且积分可以达到七位数百万级别最高积分支持九百九十九万九千九百九十九,能满足大部分积分的需求。

    计算公式为:时间戳的分数=实际分数*100000000+(99999999 –(timestamp - 开始时间timestamp))

后记

通过 redis 来存储排行榜,
第一减少了数据库的修改,减少了数据库的频繁变动。
第二减少了数据库的查询,减少了数据库的查询压力
第三加快了排名的查询速度。

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

redis实现积分排行榜 的相关文章

  • Ubuntu使用root登录系统界面、免密码、添加开机启动

    以下测试环境为Ubuntu 16 04 一 使用root登录系统界面 Ubuntu在命令行模式下 xff0c 是可以登录root的 为了安全 xff0c 在图形界面模式下 xff0c 默认不允许使用root登录系统 可以通过修改50 uni
  • 线程(Thread)的三种等待唤醒机制详解

    1 为什么需要线程的等待和唤醒 线程的等待唤醒机制是一种经典的 生产者和消费者 模型 例如食品加工厂 xff0c 食品加工人员和原料补给人员 xff0c 在有充足原料时 xff0c 补给人员是在等待 xff0c 等到原料不够时 xff0c
  • 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