应用场景:


  1. 用户签到
  2. 用户在线状态
  3. 统计活跃用户
  4. 各种状态值
  5. 自定义布隆过滤器
  6. 点赞功能

说明:

用string类型作为底层数据结构实现的一种统计状态的数据类型。

位图本质是数组,它基于string数据类型的按位操作。该数组由多个二进制位组成,每个二进制位都对应一个偏移量(可以成为一个索引或者位格)。Bitmap支持的最大位数是2^32位,它可以极大的节省存储空间,使用512M内存就可以存储42.9亿的字节信息(2^32=4294967296)

Redis 位图BitMap_redis

举例:

拿签到功能举例,比如京东的签到,估算3000万签到用户,一天一条数据,一个月就是9亿条,存储在mysql会很恐怖。

如何解决这个痛点?


  1. 一条签到记录对应一条记录,会占据越来越大的空间。
  2. 一个月最多31天,刚好int类型是32位,这样一个int类型就可以搞定一个月,32位大于31天,当天签到了就是1,没签到就是0
  3. 一条数据直接存储一个月的记录,不再存储一天的签到记录。

按年去存储一个用户的签到情况,365天只需要365/8≈46byte,1000w用户量一年也只需要44MB足够。

假如是亿级的系统,每天使用1个1亿位的Bitmap约占12M的内存(10^8/8/1024/1024),10天Bitmap的内存开销约为120MB,内存压力不算太高。在实际使用时,最好对Bitmap设置过期时间,让redis自动删除后而不再需要的签到记录节省内存开销。 

Redis 位图BitMap_数组_02

//用户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)。个人以为可以在照片表中加一个字段记录获赞总次数,这样就不用循环统计各个照片的获赞次数。