分享Redis中bittop和bitcount的有趣的实例

利用redis的bitop和bitcount来实现每日的用户量快速统计和单独用户的一段时间的登录次数

用bitop的位逻辑运算来计算每日的用户量的思路来源于一篇外文博客。

地址:https://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/

因为是篇英文博客,因此我借着这个机会锻炼英语阅读能力,同时学习一下这种运算思维逻辑,写出自己一些感悟。

一 思想来源的背景

目前很多软件中都会统计的功能,例如淘宝统计在线人数,统计每个店铺月销量,或者全民K歌中统计每一首歌在一个月内被多少用户翻唱过等等。因为这些统计往往是一种聚合,并且表述的有时很复杂,很烦多。因此用普通数据库来实现的话,有点小题大用了,而且会和磁盘I/O打上交道,一旦并发量大,又要考虑堆服务器,甚至分库分表等。

因此选用redis中间件来存放这些计算统计数据,一是因为redis中大部分的指令的时间复杂度都很小,而是所需的内存空间也很小。根据这篇外文博客中指出的:In a simulation of 128 million users, a typical metric such as “daily unique users” takes less than 50 ms on a MacBook Pro and only takes 16 MB of memory.

意思就是:在对1.28亿的用户数模拟中,就像计算每日不同用户的登录的这种典型指标,消耗了低于50ms的时间和仅仅使用了16MB的内存大小(这是基于bitmap的内存消耗)。


二 Bitmap的简要介绍

bitmap(位图)是一连串由0或1组成的二进制位,每一个位置表示一个偏移量,它提供了AND,OR,XOR等其他逻辑运算。


三 统计日用户量的思想

例如全名K歌中,某首歌(假设这首歌叫做《梦灯笼》)在一小时内被用户翻唱过一次的统计,则键可以设置 key=”梦灯笼:2020-07-23-13“,则指令为redis.setbit(key,5,1)

{width=80%}

说明:用用户id来表示key的offset,用那么上图总共16位,其中9位置为1,则使用bitcount统计,则一个小时内该歌曲被9个不同的用户翻唱过。当然用于登录次数也可以用这种表示。


四 某个用户一段时间的统计量

例如某个用户一年的登录次数,用bitmap,最大也只需要365bit的空间,键值就可以为该用户的id了。


五 使用bitop将范围延伸

如果此时有个需求,我统计了每天的数据,那么我想要按周计算每个月的某首歌的翻唱不同用户数(不叠加),这里是记录不重复的次数,因此可以用bitmap,设置偏移量,如果需要记录重复的次数,那么就要考虑用其他数据结构了,这里先不谈。

因此我们使用bitop对已有的数据做逻辑运算,例如我要获取一周内,翻唱的次数,我可以这样做,对每天的统计量进行或运算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-23 6 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-23 7 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-23 15 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-23 14 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-24 1 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-24 2 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-24 6 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-24 15 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-25 15 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-25 25 1
(integer) 0
127.0.0.1:6379> SETBIT 梦灯笼:2020-07-25 1 1

如上我对《梦灯笼》这首歌曲分别在2020-7-23~2020-7-25日期间对不同的用户模拟设定了翻唱。然后我要统计2020-7-23这天翻唱的人数,只需BITCOUNT 梦灯笼:2020-07-23

如果我要统计一周内翻唱的不同用户总数,只需要

1
2
127.0.0.1:6379> BITOP OR 梦灯笼:2020-07:4 梦灯笼:2020-07-23 梦灯笼:2020-07-24 梦灯笼:2020-07-25
(integer) 4

注:利用bitop来做逻辑运算,求得1周内总共有多少不同的用户对该首歌进行了翻唱。

如果还想要统计某一区间的用户的翻唱情况,可以使用BITCOUNT的start和end来进行约束,例如:
我想要获取用户区id区间在16,31中的翻唱情况,则可以使用BITCOUNT 梦灯笼:2020-07-25 2 4,这里的[start, end]基本单位为字节,也就是8bit,因此[2,4]表示[16,31],但是最低也只能8个为单位,因此也存在一定的局限性。

redis用于统计数据的时候,可以看出效率还是蛮高的,下图是截取了外文博客的一个时间比较。

{width=80%}