Redis 位图BitMap
应用场景:
- 用户签到
- 用户在线状态
- 统计活跃用户
- 各种状态值
- 自定义布隆过滤器
- 点赞功能
说明:
用string类型作为底层数据结构实现的一种统计状态的数据类型。
位图本质是数组,它基于string数据类型的按位操作。该数组由多个二进制位组成,每个二进制位都对应一个偏移量(可以成为一个索引或者位格)。Bitmap支持的最大位数是2^32位,它可以极大的节省存储空间,使用512M内存就可以存储42.9亿的字节信息(2^32=4294967296)
举例:
拿签到功能举例,比如京东的签到,估算3000万签到用户,一天一条数据,一个月就是9亿条,存储在mysql会很恐怖。
如何解决这个痛点?
- 一条签到记录对应一条记录,会占据越来越大的空间。
- 一个月最多31天,刚好int类型是32位,这样一个int类型就可以搞定一个月,32位大于31天,当天签到了就是1,没签到就是0
- 一条数据直接存储一个月的记录,不再存储一天的签到记录。
按年去存储一个用户的签到情况,365天只需要365/8≈46byte,1000w用户量一年也只需要44MB足够。
假如是亿级的系统,每天使用1个1亿位的Bitmap约占12M的内存(10^8/8/1024/1024),10天Bitmap的内存开销约为120MB,内存压力不算太高。在实际使用时,最好对Bitmap设置过期时间,让redis自动删除后而不再需要的签到记录节省内存开销。
//用户1,10月份第三天签到,设置为1 127.0.0.1:6379> setbit sign:u1:10 3 1 (integer) 0 127.0.0.1:6379> setbit sign:u1:10 30 1 (integer) 0 127.0.0.1:6379> getbit sign:u1:10 0 //获取10月份第一天,没签到返回0 (integer) 0 127.0.0.1:6379> getbit sign:u1:10 3 (integer) 1 127.0.0.1:6379> bitcount sign:u1:10 //签到总天数 (integer) 2 127.0.0.1:6379>
用户签到场景
每天的日期字符串作为一个key,用户Id作为offset,统计每天用户的签到情况,总的用户签到数
活跃用户数统计
用户日活、月活、留存率等均可以用redis位数组来存储,还是以每天的日期作为key,用户活跃了就写入offset为用户id的位值1。
同理月活也是如此。
用户是否在线以及总在线人数统计
同样是使用一个位数组,用户的id映射偏移量,在线标识为1,下线标识为0。即可实现用户上下线查询和总在线人数的统计
APP内用户的全局消息提示小红点
现在大多数的APP里都有站内信的功能,当有消息的时候,则提示一个小红点,代表用户有新的消息。
位图计数:
位图计数 的意思是统计bitmap中值为1的位的个数,位统计的效率时很高的。
redis中允许使用二进制的key和二进制的value,bitmap就是二进制的value。
点赞/取消点赞:
假设用户ID为100,对照片ID为100的照片进行点赞。首先根据照片ID生成数据存储的redis key,比如生成策略是like_photo:{photo_id},用户ID为1000的用户点赞只需将like_photo:100的第1000位设置为1即可(取消置为0)。
redis setbit操作的时间复杂度为O(1),所以这种点赞方式十分高效。
当前是否点赞:
用户打开图片的时候需要查询当前是否点赞过该照片,查询是否点赞可以通过redis getbit操作实现。
查询点赞总次数:
比如需要显示照片ID为1000的照片的获赞总次数,只需对like_photo:1000进行位图计数操作即可:bitcount。时间复杂度为O(N)。个人以为可以在照片表中加一个字段记录获赞总次数,这样就不用循环统计各个照片的获赞次数。