惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
S
Securelist
博客园 - Franky
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
The Cloudflare Blog
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Schneier on Security
Schneier on Security
Attack and Defense Labs
Attack and Defense Labs
Vercel News
Vercel News
腾讯CDC
Google DeepMind News
Google DeepMind News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
Netflix TechBlog - Medium
量子位
S
Schneier on Security
Hacker News: Ask HN
Hacker News: Ask HN
Cyberwarzone
Cyberwarzone
S
Security Affairs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News and Events Feed by Topic
T
Tenable Blog
PCI Perspectives
PCI Perspectives
MyScale Blog
MyScale Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
W
WeLiveSecurity
N
News | PayPal Newsroom
P
Proofpoint News Feed
O
OpenAI News
C
CERT Recently Published Vulnerability Notes
B
Blog
Cisco Talos Blog
Cisco Talos Blog
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
Y
Y Combinator Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Spread Privacy
Spread Privacy

博客园 - 飞翔在天

AI 辅助开发 - 整体 反应式编程 【转载】技术方案设计的方法论及案例分享—如何体现技术深度 云原生 性能调优 团队管理 技术管理 【生产案例】点赞数、排行榜、未读消息数(含B站方案) 【JVM】类加载器&双亲委派 【架构】单元化架构 【配置项读取】 如何写好代码 【缓存】基本理论 Lambda架构和Kappa架构 【生产案例】 【架构-案例】聊天系统 【Spring】整体 【JVM】 【Spring-WebFlux】响应式 AI ServiceComb
【生产案例】日登录用户、月登录用户统计、聚合统计
飞翔在天 · 2025-08-08 · via 博客园 - 飞翔在天

参考:https://www.cnblogs.com/uniqueDong/p/15114305.html

"统计类型:二值状态统计;聚合统计;排序统计;基数统计。
1、当我们遇到的统计场景只需要统计数据的二值状态,比如用户是否存在、 ip 是否是黑名单、以及签到打卡统计等场景就可以考虑使用Redis Bitmap
     给一个 userId ,判断用户登陆状态;

     两亿用户最近 7 天的签到情况,统计 7 天内连续签到的用户总数;

    -- 定义7个BitMap分别代表一天。并对7天数据取XOR/OR;   整体内存评估( 一个一亿个位的 Bitmap占用的内存开销,大约占 12 MB 的内存(10^8/8/1024/1024),7 天的 Bitmap 的内存开销约为 84 MB。)

   统计每天的新增与第二天的留存用户数;
2、基数统计:统计一个集合中不重复元素的个数,常见于计算独立用户数(UV)。
选型:Redis HyperLogLog(每个 HyperLogLog 最多只需要花费 12KB 内存就可以计算 2 的 64 次方个元素的基数。)
  统计网站的对访客(Unique Visitor,UV)量
  最新评论列表
  根据播放量音乐榜单"


场景:判断用户是否登陆

二值状态场景,我们就可以利用 Bitmap 来实现。比如登陆状态我们用一个 bit 位表示,一亿个用户也只占用 一亿 个 bit 位内存 ≈ (100000000 / 8/ 1024/1024)12 MB。

Bitmap 提供了 GETBIT、SETBIT 操作,通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作,需要注意的是 offset 从 0 开始。

只需要一个 key = login_status 表示存储用户登陆状态集合数据, 将用户 ID 作为 offset,在线就设置为 1,下线设置 0。通过 GETBIT判断对应的用户是否在线。 50000 万 用户只需要 6 MB 的空间。

SETBIT <key> <offset> <value>

GETBIT <key> <offset>   获取 key 的 value 在 offset 处的 bit 位的值,当 key 不存在时,返回 0。

判断 ID = 10086 的用户的登陆情况:

SETBIT login_status 10086 1  执行以下指令,表示用户已登录。

GETBIT login_status 10086  检查该用户是否登陆,返回值 1 表示已登录。

SETBIT login_status 10086 0  登出,将 offset 对应的 value 设置成 0。

场景:用户每个月的签到情况

在签到统计中,每个用户每天的签到用 1 个 bit 位表示,一年的签到只需要 365 个 bit 位。一个月最多只有 31 天,只需要 31 个 bit 位即可。

统计编号 89757 的用户在 2021 年 5 月份的打卡情况要如何进行?

key 可以设计成 uid:sign:{userId}:{yyyyMM},月份的每一天的值 - 1 可以作为 offset(因为 offset 从 0 开始,所以 offset = 日期 - 1)。

SETBIT uid:sign:89757:202105 15 1  记录用户在 2021 年 5 月 16 号打卡

GETBIT uid:sign:89757:202105 15  判断编号 89757 用户在 2021 年 5 月 16 号是否打卡

BITCOUNT uid:sign:89757:202105    统计该用户在 5 月份的打卡次数

如何统计这个月首次打卡时间呢?

Redis 提供了 BITPOS key bitValue [start] [end]指令,返回数据表示 Bitmap 中第一个值为 bitValue 的 offset 位置。

场景:连续签到用户总数

记录了一个亿的用户连续 7 天的打卡数据,如何统计出这连续 7 天连续打卡用户总数呢?

我们把每天的日期作为 Bitmap 的 key,userId 作为 offset,若是打卡则将 offset 位置的 bit 设置成 1。

key 对应的集合的每个 bit 位的数据则是一个用户在该日期的打卡记录。

一共有 7 个这样的 Bitmap,如果我们能对这 7 个 Bitmap 的对应的 bit 位做 『与』运算。

同样的 UserID offset 都是一样的,当一个 userID 在 7 个 Bitmap 对应对应的 offset 位置的 bit = 1 就说明该用户 7 天连续打卡。

结果保存到一个新 Bitmap 中,我们再通过 BITCOUNT 统计 bit = 1 的个数便得到了连续打卡 7 天的用户总数了。

Redis 提供了 BITOP operation destkey key [key ...]这个指令用于对一个或者多个 键 = key 的 Bitmap 进行位元操作。

opration 可以是 andORNOTXOR。当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被当做 0 。

空的 key 也被看作是包含 0 的字符串序列。

基数统计

统计一个集合中不重复元素的个数,常见于计算独立用户数(UV)

实现基数统计最直接的方法,就是采用集合(Set)这种数据结构,当一个元素从未出现过时,便在集合中增加一个元素;如果出现过,那么集合仍保持不变。

当页面访问量巨大,就需要一个超大的 Set 集合来统计,将会浪费大量空间。另外,这样的数据也不需要很精确,到底有没有更好的方案呢?

Redis HyperLogLog 是一种不精确的去重基数方案,它的统计规则是基于概率实现的,标准误差 0.81%,这样的精度足以满足 UV 统计需求了。

每个 HyperLogLog 最多只需要花费 12KB 内存就可以计算 2 的 64 次方个元素的基数。

Redis 对 HyperLogLog 的存储进行了优化,在计数比较小的时候,存储空间采用系数矩阵,占用空间很小。

只有在计数很大,稀疏矩阵占用的空间超过了阈值才会转变成稠密矩阵,占用 12KB 空间。

PFADD Redis主从同步原理:uv userID1 userID 2 useID3  将访问页面的每个用户 ID 添加到 HyperLogLog 中。

PFCOUNT Redis主从同步原理:uv  利用 PFCOUNT 获取 「Redis主从同步原理」页面的 UV值。

PFMERGE destkey sourcekey [sourcekey ...]  将多个 HyperLogLog 合并在一起形成一个新的 HyperLogLog 值


 最新评论列表

微信公众号的后台回复列表(不要杠,举例子),每一公众号对应一个 List,这个 List 保存该公众号的所有的用户评论。

 LPUSH 码哥字节 1 2 3 4 5 6

LRANGE key star stop 获取列表指定区间内的元素。

注意,并不是所有最新列表都能用 List 实现,对于因为对于频繁更新的列表,list类型的分页可能导致列表元素重复或漏掉。

排行榜

ZRANGEBYSCORE

ZREVRANGE


聚合统计

指的就是统计多个集合元素的聚合结果,比如说:

  • 统计多个元素的共有数据(交集);
  • 统计两个集合其中的一个独有元素(差集统计);
  • 统计多个集合的所有元素(并集统计)。

码老湿,什么样的场景会用到交集、差集、并集呢?

Redis 的 Set 类型支持集合内的增删改查,底层使用了 Hash 数据结构,无论是 add、remove 都是 O(1) 时间复杂度。

并且支持多个集合间的交集、并集、差集操作,利用这些集合操作,解决上边提到的统计问题。

交集-共同好友

比如 QQ 中的共同好友正是聚合统计中的交集。我们将账号作为 Key,该账号的好友作为 Set 集合的 value。

模拟两个用户的好友集合:

SADD user:码哥字节 R大 Linux大神 PHP之父
SADD user:大佬 Linux大神 Python大神 C++菜鸡

统计两个用户的共同好友只需要两个 Set 集合的交集,如下命令:

SINTERSTORE user:共同好友 user:码哥字节 user:大佬

命令的执行后,「user:码哥字节」、「user:大佬」两个集合的交集数据存储到 user:共同好友这个集合中。

差集-每日新增好友数

比如,统计某个 App 每日新增注册用户量,只需要对近两天的总注册用户量集合取差集即可。

比如,2021-06-01 的总注册用户量存放在 key = user:20210601 set 集合中,2021-06-02 的总用户量存放在 key = user:20210602 的集合中。

如下指令,执行差集计算并将结果存放到 user:new 集合中。

SDIFFSTORE  user:new  user:20210602 user:20210601

执行完毕,此时的 user:new 集合将是 2021/06/02 日新增用户量。

除此之外,QQ 上有个可能认识的人功能,也可以使用差集实现,就是把你朋友的好友集合减去你们共同的好友即是可能认识的人。

并集-总共新增好友

还是差集的例子,统计 2021/06/01 和 2021/06/02 两天总共新增的用户量,只需要对两个集合执行并集。

SUNIONSTORE  userid:new user:20210602 user:20210601

此时新的集合 userid:new 则是两日新增的好友。

Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞。

所以,可以专门部署一个集群用于统计,让它专门负责聚合计算,或者是把数据读取到客户端,在客户端来完成聚合统计,这样就可以规避由于阻塞导致其他服务无法响应。