亿级活跃用户场景
- 实现日活跃统计
- 实现周活跃统计
- 为了增强用户粘性,增加一个连续打卡发放积分功能,怎么实现连续打卡用户统计。
传统做法
定义一个表,记录每个用户的每次登陆时间
user_id | login_day |
---|---|
400 | 2019-10-22 |
500 | 2019-10-22 |
1000 | 2019-10-21 |
统计2019-10-21和2019-10-22两天有登陆的用户数量sql
select count(*) from login_stats today_stats inner join login_stats yesterday_stats on
today_stats.user_id = yesterday_stats.user_id
where
today_stats.login_day = '2019-10-22' and
yesterday_stats.login_day = '2019-10-21';
在千万级甚至亿级日活跃的应用中,这张表每天新增数据量就会达到千万级甚至亿级规模,大表的操作性能是非常差的。
Redis bitmap
在使用getrange和setrange对字符串操作时,是基于字节实现的。
Redis提供更加精细的操作,可以针对某个字节的bit位进行操作。
bit位的值只有两个,0 和 1.
Redis操作指令
setbit key offset 0|1 # offset为索引,能精确定位到某个bit
getbit key
bitcount key
bitop and|or key
一个例子
some_value:
0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ...
offset:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...
每个offset对应一个bit
offset的上限是 2^32-1 约等于 42亿
setbit的过程,value值会发生动态扩容。
127.0.0.1:6379> setbit key 1 1 # 第一次设置值,offset为1,处于第一个字节的第2个bit,值只需要分配一个字节
(integer) 0
127.0.0.1:6379> strlen key
(integer) 1
127.0.0.1:6379> setbit key 8 1 # 第二次设置值,offset为8,处于第二个字节的第1个bit,值发生扩容.
(integer) 0
127.0.0.1:6379> strlen key
(integer) 2
如何实现文中开头的三个场景统计
首先用户id必须是个整型,如果用户id起始值过大,可以做一个偏移;用户id不全是整型,可以用hash算法得出自然数然后取模。 把用户id作为 offset。
计算日活跃
# 设置100 103 109用户在2019:0521登录
127.0.0.1:6379> setbit login:2019:0521 100 1
(integer) 0
127.0.0.1:6379> setbit login:2019:0521 103 1
(integer) 0
127.0.0.1:6379> setbit login:2019:0521 109 1
(integer) 0
127.0.0.1:6379> bitcount login:2019:0521 0 -1 # 统计2019:0521登录过的用户
(integer) 3
# 设置100 109用户在2019:0522登录
127.0.0.1:6379> setbit login:2019:0522 100 1
(integer) 0
127.0.0.1:6379> setbit login:2019:0522 109 1
(integer) 0
127.0.0.1:6379> bitcount login:2019:0522 # 统计2019:0522登录过的用户
(integer) 2
# 设置100用户在2019:0523登录
127.0.0.1:6379> setbit login:2019:0523 100 1
(integer) 0
127.0.0.1:6379> bitcount login:2019:0523 # 统计2019:0523登录过的用户
(integer) 2
计算连续多日活跃人数
# 连续两日
127.0.0.1:6379> bitop and login:2019:0521 login:2019:0522
(integer) 14
127.0.0.1:6379> bitop and login:2019:0521-0522 login:2019:0521 login:2019:0522
(integer) 14
127.0.0.1:6379> bitcount login:2019:0521-0522
(integer) 2
# 连续三日
127.0.0.1:6379> bitop and login:2019:0521-0523 login:2019:0521 login:2019:0522 login:2019:0523
(integer) 14
127.0.0.1:6379> bitcount login:2019:0521-0523
(integer) 1
计算多日其中一日有活跃的人数,例如周活跃统计
127.0.0.1:6379> bitop or orlogin:2019:0521-0523 login:2019:0521 login:2019:0522 login:2019:0523
(integer) 14
127.0.0.1:6379> bitcount orlogin:2019:0521-0523
(integer) 3
offset开辟1亿所需字节数
127.0.0.1:6379> setbit key1e 100000000 1
(integer) 0
127.0.0.1:6379> strlen key1e
(integer) 12500001
127.0.0.1:6379> type key1e
string
127.0.0.1:6379> object encoding key1e
"raw"
字节数为 1250w Byte,约等于12MB,还是很省内存的。
使用注意事项
- bitcount操作的时间复杂度是O(n),在大并发或者offset非常大的情况下使用会有性能问题。
- bitmap适合海量数据场景下使用,如果要统计的数据不多,或者offset比较分散,会占用较多的内存,可能使用关系型数据库统计较好。
- 其实只要是记录一些二元操作,都可以使用bitmap统计。
网友评论