触发点
用
MySql
来实现的话虽然简单粗暴,但是也有弊端,比如我们想要做一些复杂的功能就不是太方便了,或者说不是太高性能了,比如,今天是连续签到的第几天,在一定时间内连续签到了多少天。另外一方面,如果按 100 万用户量级来计算,一个用户每年可以产生365
/366
条记录,100 万用户的所有签到记录那就有点恐怖了,查询计算速度也会越来越慢。
准备
Redis
的字符串数据都是以二进制的形式存放的,所以说Redis
的 Bit 操作非常适合处理这个场景,因为Bit
的值为0
或1
,用户是否打卡也可以用 0 或 1 来表示,我们把签到的天数对应到每个字节上,打卡了就是1
,没打卡就是0
,那么一个用户一年下来的记录就是365
位的长度,100
万用户一年只需要耗费大约43 M
左右的存储空间就可以了,而且速度贼快
大伙可能会问,这个究竟是怎么计算来的,我们来看一下官方的解释:
在一台 2010MacBook Pro 上,offset 为 2^32-1(分配 512MB)需要~300ms,offset 为 2^30-1 (分配 128MB) 需要~80ms,offset 为 2^28-1(分配 32 MB)需要~30ms,offset 为 2^26-1(分配 8MB)需要 8ms。
大概的空间占用计算公式是:(offset / 8 / 1024 / 1024) MB
实例
- 实例化一个
Redis
连接
1 |
|
1 | $redis = RedisTool::getInstance(); |
- 如何设计
key
?
1 | $dayKey = 'login:' . \now()->format('Ymd'); |
- 签到
http://redisdoc.com/bitmap/setbit.html
setbit
: 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
1 | $redis->setBit($dayKey, $user_id, 1); |
- 统计签到数据
http://redisdoc.com/bitmap/bitop.html
setOp
: 对一个或多个保存二进制位的字符串key
进行位元操作,并将结果保存到destkey
上
AND
: 对一个或多个key求逻辑并
OR
: 对一个或多个key求逻辑或
XOR
: 对一个或多个key求逻辑异或
NOT
: 对给定的key求逻辑非
1 | $redis->bitOp('AND', 'threeAnd', 'login:20190804', 'login:20190805', 'login:20190806'); |
- 结果
1 | 连续三天都签到的用户数量: 1 |
总结
- 这里只针对用户签到统计, 如需用户签到具体信息,需要使用一个签到记录表