Redis中bitmap的应用
redis中bitmap的相关方法
- SETBIT(key offset value)
对
key
所储存的字符串值,设置或清除指定偏移量上的位(bit)。
offset是下标,value是0或者1,复杂度为O(1)
- GETBIT(key offset)
对
key
所储存的字符串值,获取指定偏移量上的位(bit)。
获得offset上的值,0或者1,复杂度为O(1)
- BITCOUNT
计算给定字符串中,被设置为
1
的比特位的数量。
例如这个key保存的字符串二进制为101011,则返回结果4。
- BITOP(operation destkey key [key ...])
对一个或多个保存二进制位的字符串key
进行位元操作,并将结果保存到destkey
上。
operation
可以是AND
、OR
、NOT
、XOR
这四种操作中的任意一种
对两个二进制串进行操作用的方法。
- BITFIELD
3.2版本新加入的方法,功能很复杂。这里不做讨论
以上几个方法结合使用,可以实现以下几种功能:
1、统计每日上线人数(活跃人数统计)
用带有日期的字符串当key
,拿用户的uid
当作offset,当天登录了就执行一次下面的代码
$redis->setBit('20170323_au',123,1);
这样,所有登录过的用户,会组成这个key
为“20170323_au”的二进制字符串中
然后再使用
$redis->bitCount('20170323_au');
就可以拿到今天上线人数的统计了。
如果你想拿到最近三天的活跃人数怎么办?同样简单:
$redis->bitOp('OR','last_3_au','20170321_au','20170322_au','20170323_au');
$result = $redis->bitCount('last_3_au');
用BITOP
将三天的结果做OR
操作
得到的结果在bitCount
一下就是最近三天的活跃人数了。
关于效率,有人统计了128million用户的表现。
PERIOD | TIME (MS) |
---|---|
Daily | 50.2 |
Weekly | 392.0 |
Monthly | 1624.8 |
对比传统的数据库,实现起来的难易度和效率简直不可同日而语。
下面是我自己的测试数据
bitOp('OR', 'stat_3_day_3m', '20161221', '20161222', '20161223');
echo $redis->bitCount('stat_3_day_3m') . "\n";
echo "time taken: " . (microtime(true) - $t1);
function makeTestData($key)
{
$redis = RedisManage::getRedisForShare();
for ($i = 0; $i < 3000000; $i++) {
$redis->setBit($key, $i, mt_rand(0, 1));
}
}
先创建了3条3million的数据。
最后显示结果:
而且这是windows环境下的测试结果。linux应该会更快。
这要比传统的关系型数据库使用count,group by要效率很多。
而且,因为是bitmap的原因,300万个用户的数据占用的长度为3000000/8=375000字节,也就是45kb不到。
1年也就是16.3Mb的数据。
2、用来判断当天是否是第一次登录(或者每天限领一次的逻辑判断)
之前游戏的经验,是通过数据库中的一个column来记录上一次登录的时间戳,再用当前时间跟数据库(MySQL)中记录的时间做对比来判断是否是同一天登录。比较传统的做法。
这里可以同样利用上面的方法,
$login_status = $redis->getBit('20170323', $uid);
if($login_status == 0){
// first login today and send login gifts
}
3、同来做连续登陆奖励发放的条件判断
同样,用来做连续登陆的判断也很方便,只需要将之前几天的登录记录做一下and
操作,在做判断就可以了。
$redis->bitOp('AND', 'stat_3_day_continue', '20161221', '20161222', '20161223');
$is_3_day_continue = $redis->getBit('stat_3_day_continue', $uid);
if($is_3_day_continue){
// three days login gifts send
}
结语
只要是涉及到类似签到功能的记录,无论是以什么时间间隔为单位(最终就是反应在key的不同),都可以使用Redis的bitmap数据类型。无论是从效率上,还是资源上,都有很大的优势。