用Redis bitmap统计活跃用户、留存 刺骨的言语ヽ痛彻心扉 2022-01-14 14:05 177阅读 0赞 Spool的开发者博客,描述了Spool利用Redis的bitmaps相关的操作,进行网站活跃用户统计工作。 原文:http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/ Redis支持对String类型的value进行基于二进制位的置位操作。通过将一个用户的id对应value上的一位,通过对活跃用户对应的位进行置位,就能够用一个value记录所有活跃用户的信息。如下图所未,下图中的bitmap有9个位被置为1,表示这9个位上对应的用户是今天的活跃用户。其中第15位表示uid为15的用户,第一位表示uid为0的用户。(如果你的uid不是从1开始的,比如从100000开始,实际上你也可以相应的用uid减去初始值来表示其位数,比如1000000用户对应到bitmap的第一位) ![NoSQLFan:用Redis bitmap统计活跃用户][NoSQLFan_Redis bitmap] 具体的代码类似下面这样: redis.setbit(play:yyyy \- mm \- dd, user\_id , **1** ) 这样一次记录的复杂度是O(1),在Redis中速度非常快。 而我们通过每天换用一个不同的key来将每天的活跃用户状态记录分开存。并且可以通过一些与或运算计算出N天活跃用户,和连接N天活跃用户这样的统计数据。 如下图,第一行表示星期一的活跃用户情况,第二行表示周二的,以此类推。为样我们通过对N天的活跃用户记录取并集操作,就能得出在N天内活跃过的用户列表。 ![NoSQLFan:用Redis bitmap统计活跃用户][NoSQLFan_Redis bitmap 1] 下面表格表示对应一天,一周,一个月统计时所花费的时间。 ![NoSQLFan:用Redis bitmap统计活跃用户][NoSQLFan_Redis bitmap 2] 下面是具体的java代码片断: 算出一天的活跃用户数量 import redis.clients.jedis.Jedis; import java.util.BitSet; ... Jedis redis = new Jedis( " localhost " ); ... public int uniqueCount(String action, String date) \{ String key = action \+ " : " \+ date; BitSet users = BitSet.valueOf(redis.get(key.getBytes())); return users.cardinality(); \} 计算某几个内活跃用户的数量(某一天活跃就算,所以是取并集) import redis.clients.jedis.Jedis; import java.util.BitSet; ... Jedis redis = new Jedis( " localhost " ); ... public int uniqueCount(String action, String... dates) \{ BitSet all = new BitSet(); for (String date : dates) \{ String key = action \+ " : " \+ date; BitSet users = BitSet.valueOf(redis.get(key.getBytes())); all.or(users); \} return all.cardinality(); \} 具体的用法还很多,比如你还可以对独特终端的用户单独记一个bitmap,这样就可以统计不同终端用户的活跃情况。有的同学会说用set也能实现同样的效果。但使用set在[内存][Link 1]使用量上是会大很多的。 ========================================================================== 看完这篇文章后,我测试了一下: redis> SETBIT bit 10086 1 (integer) 0 redis> GETBIT bit 10086 (integer) 1 对使用大的offset的 SETBIT 操作来说,第一次内存分配可能造成 Redis 服务器被阻塞.因为Redis需要生成很长的二进制系列。 **问题:** 如果活跃用户在百万级别,使用Redis BitMap很划算。 如果如果活跃用户很少,而用户id都是10位以上的int。那就很浪费内存了。那还不如使用set集合呢。然后求交集就可以了。 我们可以计算内存:offset = 999 999 999 =》需要的内存999 999 999/8/1024/1024 = 119M左右。 如果统计的数据还有很多维度,且维度组合有上千种,使用这个方式就不划算。我们可以借鉴bitmap使用另外的方式来统计活跃留存: 留存的指标: 次日注册留存、 2日注册留存... N日注册留存, 比如昨天注册了1000名用户中,在今天有300名用户又登录了,那么对应于昨天的注册留存就是30%; 从总体上看,这些指标依赖于核心变量——用户访问时间。 那我们可以使用bitMap来记录用户访问时间: 如果我们统计时间是从2013年开始,那么2013-01-01就是bit的第一位...以此类推, 2013年的最后一天,即是bit位的第365位。 这样我们已经记录用户所有天的是否登录。 然后我们计算留存: 留存计算: 1) 计算当天时间,对应对应的bit位,如今他是7月01日,bit位是182. 2)次日留存: 查看bit的(182-1)=181位是否存在,若存在,留存数+1 N日留存: 查看bit的(182-n)位是否存在,若存在,n日留存数+1 我们再来估算占用空间。一年365bit位。1000万用户,占用的空间=1000万 \* 365bit/8 /1024/1024 = 430M [NoSQLFan_Redis bitmap]: /images/20220114/276f17666ca54989b025b364e33ad116.png [NoSQLFan_Redis bitmap 1]: /images/20220114/7ae58841e9c44e9ea043abcd0b1d805d.png [NoSQLFan_Redis bitmap 2]: /images/20220114/d2757f72ea1c4443a89c0a4154e50feb.png [Link 1]: http://product.it168.com/list/b/0205_1.shtml
还没有评论,来说两句吧...