使用 Redis 统计在线用户人数

2023-11-11

在构建应用的时候, 我们经常需要对用户的一举一动进行记录, 而其中一个比较重要的操作, 就是对在线的用户进行记录。

本文将介绍四种使用 Redis 对在线用户进行记录的方案, 这些方案虽然都可以对在线用户的数量进行统计, 但每个方案都有一些自己特有的操作, 并且各个方案的性能特征以及资源消耗也各有不同。

../../_images/online_users.png

方案 1 :使用有序集合

每当一个用户上线时, 我们就执行 ZADD 命令, 将这个用户以及它的在线时间添加到指定的有序集合中:

ZADD "online_users" <user_id> <current_timestamp>

通过使用 ZSCORE 命令检查指定的用户 ID 在有序集合中是否有相关联的分值, 我们可以知道该用户是否在线:

ZSCORE "online_users" <user_id>

而通过执行 ZCARD 命令, 我们可以知道总共有多用户在线:

ZCARD "online_users"

使用有序集合储存在线用户的强大之处在于, 它是本文介绍的所有方案当中, 能够执行最多聚合操作的一个方案, 原因在于, 这一方案既可以通过有序集合的成员(也即是用户的 ID)进行聚合操作, 也可以根据有序集合的分值(也即是用户的登录时间)进行聚合操作。

首先, 通过 ZINTERSTOREZUNIONSTORE 命令, 我们可以对多个记录了在线用户的有序集合进行聚合计算:

# 计算出 7 天之内都有上线的用户,并将它储存到 7_days_both_online_users 有序集合当中
ZINTERSTORE 7_days_both_online_users 7 "day_1_online_users" "day_2_online_users" ... "day_7_online_users"

# 计算出 7 天之内总共有多少人上线了
ZUNIONSTORE 7_days_total_online_users 7 "day_1_online_users" ... "day_7_online_users"

此外, 通过 ZCOUNT 命令, 我们可以统计出在指定的时间段之内有多少用户在线, 而 ZRANGEBYSCORE 命令则可以让我们获取到这些用户的名单:

# 统计指定时间段内上线的用户数量
ZCOUNT "online_users" <start_timestamp> <end_timestamp>

# 获取指定时间段内上线的用户名单
ZRANGEBYSCORE "online_users" <start_timestamp> <end_timestamp> WITHSCORES

通过这一方法, 我们可以知道网站在不同时间段的上线人数以及上线用户名单, 比如说, 我们可以用这个方法来分别获知网站在早晨、上午、中午、下午和夜晚的上线人数。

方案 2 :使用集合

正如上一节所说, 使用有序集合能够同时储存在线用户的名单以及各个用户的上线时间, 但如果我们只想要记录在线用户的名单, 而不想要储存用户的上线时间, 那么也可以使用集合来代替有序集合, 对在线的用户进行记录。

在这种情况下, 每当一个用户上线时, 我们就执行以下 SADD 命令, 将它添加到在线用户名单当中:

SADD "online_users" <user_id>

通过使用 SISMEMBER 命令, 我们可以检查一个指定的用户当前是否在线:

SISMEMBER "online_users" <user_id>

而统计在线人数的工作则可以通过执行 SCARD 命令来完成:

SCARD "online_users"

通过集合运算操作, 我们可以像有序集合方案一样, 对不同时间段或者日期的在线用户名单进行聚合计算。 比如说, 通过 SINTER 或者 SINTERSTORE 命令, 我们可以计算出一周都有在线的用户:

SINTER "day_1_online_users" "day_2_online_users" ... "day_7_online_users"

此外, 通过 SUNION 命令或者 SUNIONSTORE 命令, 我们可以计算出一周内在线用户的总数量:

SUNION "day_1_online_users" "day_2_online_users" ... "day_7_online_users"

而通过执行 SDIFF 命令或者 SDIFFSTORE 命令, 我们可以知道哪些用户今天上线了, 但是昨天没有上线:

SDIFF "today_online_users" "yesterday_online_users"

又或者工作日上线了, 但是假日没有上线:

# 计算工作日上线名单
SINTERSTORE "weekday_online_users" "monday_online_users" "tuesday_online_users" ... "friday_online_users"
# 计算假日上线名单
SINTERSTORE "holiday_online_users" "saturday_online_users" "sunday_online_users"
# 计算工作日上线但是假日未上线的名单
SDIFF "weekday_online_users" "holiday_online_users"

诸如此类。

方案 3 :使用 HyperLogLog

虽然使用有序集合和集合能够很好地完成记录在线人数的工作, 但以上这两个方案都有一个明显的缺点, 那就是, 这两个方案耗费的内存会随着被统计用户数量的增多而增多: 如果你的网站用户数量比较多, 又或者你需要记录多天/多个时段的在线用户名单并进行聚合计算, 那么这两个方案可能会消耗你大量内存。

另一方面, 在有些情况下, 我们只想要知道在线用户的人数, 而不需要知道具体的在线用户名单, 这时有序集合和集合储存的信息就会显得多余了。

在需要尽可能地节约内存并且只需要知道在线用户数量的情况下, 我们可以使用 HyperLogLog 来对在线用户进行统计: HyperLogLog 是一个概率算法, 它可以对元素的基数进行估算, 并且每个 HyperLogLog 只需要耗费 12 KB 内存, 对于用户数量非常多但是内存却非常紧张的系统, 这一方案无疑是最佳之选。

在这一方案下, 我们使用 PFADD 命令去记录在线的用户:

PFADD "online_users" <user_id>

使用 PFCOUNT 命令获取在线人数:

PFCOUNT "online_users"

因为 HyperLogLog 也提供了计算交集的 PFMERGE 命令, 所以我们也可以用这个命令计算出多个给定时间段或日期之内, 上线的总人数:

# 统计 7 天之内总共有多少人上线了
PFMERGE "7_days_both_online_users" "day_1_online_users" "day_2_online_users" ... "day_7_online_users"
PFCOUNT "7_days_both_online_users"

方案 4 :使用位图(bitmap)

回顾上面介绍的三个方案, 我们可以得出以上结论:

  • 使用有序集合或者集合能够储存具体的在线用户名单, 但是却需要消耗大量的内存;
  • 而使用 HyperLogLog 虽然能够有效地减少统计在线用户所需的内存, 但是它却没办法准确地记录具体的在线用户名单。

那么是否存在一种既能够获得在线用户名单, 又可以尽量减少内存消耗的方法存在呢? 这种方法的确存在 —— 使用 Redis 的位图就可以办到。

Redis 的位图就是一个由二进制位组成的数组, 通过将数组中的每个二进制位与用户 ID 进行一一对应, 我们可以使用位图去记录每个用户是否在线。

当一个用户上线时, 我们就使用 SETBIT 命令, 将这个用户对应的二进制位设置为 1 :

# 此处的 user_id 必须为数字,因为它会被用作索引
SETBIT "online_users" <user_id> 1

通过使用 GETBIT 命令去检查一个二进制位的值是否为 1 , 我们可以知道指定的用户是否在线:

GETBIT "online_users" <user_id>

而通过 BITCOUNT 命令, 我们可以统计出位图中有多少个二进制位被设置成了 1 , 也即是有多少个用户在线:

BITCOUNT "online_users"

跟集合一样, 用户也能够对多个位图进行聚合计算 —— 通过 BITOP 命令, 用户可以对一个或多个位图执行逻辑并、逻辑或、逻辑异或或者逻辑非操作:

# 计算出 7 天都在线的用户
BITOP "AND" "7_days_both_online_users" "day_1_online_users" "day_2_online_users" ... "day_7_online_users"

# 计算出 7 在的在线用户总人数
BITOP "OR" "7_days_total_online_users" "day_1_online_users" "day_2_online_users" ... "day_7_online_users"

# 计算出两天当中只有其中一天在线的用户
BITOP "XOR" "only_one_day_online" "day_1_online_users" "day_2_online_users"

HyperLogLog 方案记录一个用户是否在线需要花费 1 个二进制位, 对于用户数为 100 万的网站来说, 使用这一方案只需要耗费 125 KB 内存, 而对于用户数为 1000 万的网站来说, 使用这一方案也只需要花费 1.25 MB 内存。

虽然位图节约内存的效果不及 HyperLogLog 那么显著, 但是使用位图可以准确地判断一个用户是否上线, 并且能够像集合和有序集合一样, 对在线用户名单进行聚合计算。 因此对于想要尽量节约内存, 但又需要准确地知道用户是否在线, 又或者需要对用户的在线名单进行聚合计算的应用来说, 使用位图可以说是最佳之选。

总结

以下表格总结了以上四个方案的特点:

方案 特点
有序集合 能够同时储存在线用户的名单以及用户的上线时间,能够执行非常多的聚合计算操作,但是耗费的内存也非常多。
集合 能够储存在线用户的名单,也能够执行聚合计算,消耗的内存比有序集合少,但是跟有序集合一样,这个方案消耗的内存也会随着用户数量的增多而增多。
HyperLogLog 无论需要统计的用户有多少,只需要耗费 12 KB 内存,但由于概率算法的特性,只能给出在线人数的估算值,并且也无法获取准确的在线用户名单。
位图 在尽可能节约内存的情况下,记录在线用户的名单,并且能够对这些名单执行聚合操作。

因为 Redis 同时支持多种数据结构, 所以一个问题常常可以在 Redis 里面找多种不同的解法, 并且每种解法都有各自的优点和缺点, 本文介绍的问题就是一个很好的例子。

关于统计在线用户的方法就介绍到这里, 希望这些方案会给大家带来帮助和启发。

转载于:https://www.cnblogs.com/mr-amazing/p/6245421.html

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

使用 Redis 统计在线用户人数 的相关文章

随机推荐

  • 基于GRU门控循环网络的时间序列预测matlab仿真,对比LSTM网络

    目录 1 算法运行效果图预览 2 算法运行软件版本 3 部分核心程序 4 算法理论概述 5 算法完整程序工程 1 算法运行效果图预览 LSTM GRU 2 算法运行软件版本 matlab2022a 3 部分核心程序 构建GRU网络模型 la
  • 如何通过Geth、Node.js和UNIX/PHP访问以太坊节点

    本文旨在说明通过Geth Node js如何访问以太坊节点和UNIX下PHP如何访问以太坊节点 说明如何通过RPC使用此 A 以太坊节点 对于以太坊主网络使用RPC url http 85 214 51 53 8545 对于Ropsten测
  • 线上生产问题系列之-@Async使用不当引发的血案

    现象描述 突然客户群里反馈 线上某功能处理出现严重拥堵 再处理不好就要切换渠道 这个功能就是一个通知功能 客户依赖通知结果去完成他的业务逻辑 但是这个通知非常缓慢 严重拥堵 背景描述 常有这样一个需求场景 为了提高请求的吞吐量 在一个请求链
  • 奇偶调序

    题目描述 输入一个整数数组 调整数组中数字的顺序 使得所有奇数位于数组的前半部分 所有偶数位于数组的后半部分 要求时间复杂度为O n 分析与解法 最容易想到的办法是从头扫描这个数组 每碰到一个偶数 拿出这个数字 并把位于这个数字后面的所有数
  • Scala针对容器的操作(遍历、映射、过滤、归约)案例

    Scala针对容器的操作 遍历 映射 一 遍历操作 二 映射操作 2 1 map方法 2 2 flatmap方法 三 过滤操作 四 归约操作 一 遍历操作 Scala容器的标准遍历方法foreach def foreach U f Elem
  • react+ts+echarts5.x按需导入实现世界地图

    registerMap注册世界地图 1 获取世界地图geoJSON格式的文件 获取地图的渠道 这个步骤很重要 本人找了很久都没找到世界地图的GeoJSON文件 这个网址可以提供 并且也提供了各个国家的GeoJSON a 根据 在线实例 确定
  • 前端js采坑,一个函数中同时有多个ajax()异步请求

    在近期的项目中 问题 多个异步请求执行时 有两个请求的路径是相同的 导致结果只执行当中的一个异步请求 add function vm showList false vm title 新增 vm role deptName null dept
  • 微服务架构-Day7

    学习目标 学会微服务架构 对应项目hotel demo 学习笔记 1 数据聚合 聚合 aggregations 可以让我们极其方便的实现对数据的统计 分析 运算 实现这些统计功能的比数据库的sql要方便的多 而且查询速度非常快 可以实现近实
  • display设为inline-block时引发的高度问题,大坑

    今天在写小程序 点击让这个遮罩层显示 结果一直下移 莫名其妙 解决方案 在元素的CSS中添加 vertical align bottom
  • SQL-使用视图

    什么是视图 它们怎样工作 何时使用它们 如何利用视图简化执行的某些SQL操作 1 使用视图的原因 A 重用SQL语句 B 简化复杂的SQL操作 在编写查询后 可以方便地重用它而不必知道其基本查询 C 使用表的一部分而不是整个表 D 保护数据
  • 【Python】科学计算库Scipy简易入门

    0 导语 Scipy是一个用于数学 科学 工程领域的常用软件包 可以处理插值 积分 优化 图像处理 常微分方程数值解的求解 信号处理等问题 它用于有效计算Numpy矩阵 使Numpy和Scipy协同工作 高效解决问题 Scipy是由针对特定
  • vue-组件按需加载

    组件按需加载 路由配置 path name component gt import views vue 按需加载 在vue中配置路由时 可以在头部先引入组件 然后下面定义路由时 在指向到具体使用的组件 这种是页面运行时 组件全部加载 占内存
  • 严重: 子容器启动失败 java.util.concurrent.ExecutionException 信息: 正在摧毁协议处理器 ["http-nio-80"]WARNING: An illegal

    话不多说直接上错误 解决方案 由于一开始以为是tomcat和eclipseEE出现故障 将两个软件重新下载并配置环境但错误没有解决 然后又检查了JDK版本也没问题 最后肯定了是代码的问题 仔细检查后发现是servlet映射地址写重了 后来又
  • HAL库的RCC简介

    一 RCC的时钟树总览 时钟输入源有四个 选择器 预 分频器和倍频器 最终设置的频率 SYSCLK系统时钟 SYSCLK可以有三种方式得到 1 HSI内部高速时钟用的是RC振荡器 频率为8M 精度不高 没有经过分频器和倍频器 这种方式得到的
  • 空utf8文件占三字节的问题(Java空文本文件FileInputStream读取问题)

    1 文件创建情况 2 程序代码 public class Demo01 public static void main String args throws IOException File file new File a txt long
  • pycharm mysql 安装_pycharm安装mysql驱动包

    新的环境配置pycharm的项目时 发现pycharm不能连接到mysql数据库 由于安了java环境但是还没配置相关的库 并且jetbrains家的IDE一般都是java写的 于是猜想可能是java缺少mysql的驱动 1 先确保pyth
  • c++学习:2.变量声明和定义的关系

    为了支持分离式编译 c 语言将声明和定义区分开来 声明只有名字并无实体 定义创建于声明名字相关的实体 因此声明和定义最重要的区别 声明不申请存储空间 定义申请存储空间 变量能且只能被定义一次 但是可以被多次声明 注意这里说的变量定义和变量赋
  • Pytorch 自己搭建的一个神经网络

    目录 数据集 dogs Vs Cats import time import torch nn as nn import torch optim from torch autograd import Variable from torch
  • 数学建模之圈养湖羊的空间利用率

    数学建模之圈养湖羊的空间利用率 D题最新最全思路已出 欢迎后台私信咨询 1 问题 规模化的圈养养殖场通常根据牲畜的性别和生长阶段分群饲养 适应不同种类 不同阶段的牲畜对空间的不同要求 以保障牲畜安全和健康 与此同时 也要尽量减少空间闲置所造
  • 使用 Redis 统计在线用户人数

    在构建应用的时候 我们经常需要对用户的一举一动进行记录 而其中一个比较重要的操作 就是对在线的用户进行记录 本文将介绍四种使用 Redis 对在线用户进行记录的方案 这些方案虽然都可以对在线用户的数量进行统计 但每个方案都有一些自己特有的操