在我们日常玩的游戏中或一些银行类app上,他们为了提高用户的活跃度,设计了签到领取奖励的功能,当用户连续签到多少天之后我们可以获得相应的奖励,如下是某个app中签到页面:

用户签到的数据如果直接存到数据库中是不推荐的,因为需要一张签到表来单独的记录每个用户的每天签到数据,随着业务的发展,一旦用户量非常大的时候,那么签到表的数据会急剧的增长,到最后会存在性能的问题。下面我们来介绍使用redis的bitmap来实现记录和统计用户连续签到的方案。
1、认识redis的bimap
bitmap实际上就是由一个一个的二进制位所组成的,在bitmap中每一个位只存放0或者1,如下所示的bitmap结构图:

Redis的bitmap是字符串类型实现的,我们知道字符串类型最大支持512M,换算成二进位可以存放大约42亿个bit位,如下的换算过程:

所以即使网站中有几亿日活的用户,当需要统计他们的连续签到时,bitmap也是可以记录的并且所占的内存也是很小的。
2、bitmap实现用户连续签到功能
使用bitmap存储用户的连续签到功能有两个存储维度,第一个是按照日期的维度统计,第二个是按照用户的维度来统计。
2.1 日期维度统计
如果要统计用户在12月份的连续签到次数,我们设置key为日期,用户的id作为bitmap上的偏移位,如下所示:

这里要求用户的id必须是数字的,如果用户的id不是数字的话,可以使用用户id的hash值加随机数的方式将用户id映射成数字,然后存放到bitmap上。
如果以日期为维度来存储签到数据,要统计12月份所有用户的连续签到,那么就需要31个bitmap就是可以实现。假设用户id等于8的用户签到了,我们将bitmap上8这个位置的0修改成1,如下所示:

同样的道理,id为8的用户在12月份每天的签到数据都存储到这31个bitmap上,如下图所示:


如果只是统计用户在12月份签到的天数,我们依次向上遍历,遇到1就增加签到的次数,遇到0就不统计。如果是连续签到的次数,那么遇1就累加签到的次数,遇0则重新统计。
按照日期为维度的方式统计每天的所有用户签到的次数,效率是比较高,但是如果要统计单个用户的话,还是要去循环每一个日期的key。
这种方式适用于用户量比较大的场景,当然建议统计不超过一个月的数据,因为统计的日期太长的话,对应的key数量也是越来越多的,需要的内存也会增加。如果只统计一个月的签到数据,只需要将当前的这个key过期时间设置为一个月,当超过一个月会自动的过期,这样可以有效的利用redis的内存。
2.2 用户维度的统计
这种方式为每个用户分配一个bitmap,用户id为key,将日期映射到bitmap上,如下图所示:

假设id为8的用户连续5天签到了,那么我们记录在bitmap中的数据如下所示:

这种方式很适合统计单个用户的连续签到情况,但是要统计每天所有用户的签到就需要遍历每个用户的bitmap,此时的效率比较低。
总结:
(1)bitmap底层是由二进制位组成的,其bit位上非0即1。
(2)像记录和统计登录、签到,点赞等二值场景并且涉及到海量数据都可以使用bitmap来实现,因为bitmap不仅占用的内存非常小,而且统计速度非常快。




