十六、Redis三种特殊类型之三Bitmap

一、BitMap是什么

通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身,value对应0或1,我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间

二、Redis中的BitMap

Redis从2.2.0版本开始新增了setbit、getbit、bitcount等几个bitmap相关命令。虽然是新命令,但是并没有新增新的数据类型,因为setbit等命令只不过是在set上的扩展。

Redis的bitmap让我们可以实时的进行统计,并且极其节省空间。在模拟1亿2千8百万用户的模拟环境下,在一台MacBookPro上,典型的统计如“日用户数”(dailyunique users) 的时间消耗小于50ms, 占用16MB内存。

三、Redis中的命令

常用命令 作用
1、getbit key offset 用于获取Redis中指定key对应的值,中对应offset的bit
2、setbit key key offset value 用于修改指定key对应的值,中对应offset的bit
3、 bitcount key [start end] 用于统计字符串被设置为1的bit数
4、bitop and/or/xor/not destkey key [key …] 用于对多个key求逻辑与/逻辑或/逻辑异或/逻辑非

四、bitmap的应用场景

bitmap优势很多,用一个例子详细说明下。

-------------------------------------------------------------------------------------------

用redis的bitmap方式统计上亿访问量的周活跃用户

(1)提出问题

网站每天有1亿的访问量,产品提出要统计每个uid的周活跃,目前是日志分析解决的,每天有20G的日志,公司有dip平台会用日志去计算,每次要计算两小时才能处理完。

(2)分析问题

考虑了一下是否可以用redis的bitmap的方式来做一个统计周活跃的功能

先简单说下bitmap的原理:
假设有3个同学:

张三 李四 王五

如果有三间房,0是男,女是1,

房1 房2 房2
0 1 1

如果要统计现在班上有几位女生,就可以看到两个1就是两位女生

在计算机里,一个字节里有8个二进制位,即1byte=8bit, 一个int类型是4bytes
假设有7个数字,我们可以按照编号放进一段连续内存里,对应位置中存在就显示1,其它默认都显示0
比如6 ,3 ,8 ,32 ,36
那对应的位置为:

十六、Redis三种特殊类型之三Bitmap

如何判断int数字在tmp数组的那个位置?
  例:
  1) 8 / 32 = 0 整数8除以32得整数部分为0,那么整数8在tmp[0]的数组上;
  2) 8%32 = 8 整数8模32得8,那么整数8在tmp[0]数组 从右向左的 第八个位置上(位置计算从0开始计数,数到8的位置)

十六、Redis三种特殊类型之三Bitmap

如果两亿的数字做排序排重,我们大概要占用好几G的空间,如果用bitmap方式,最少只需要200000000/8/1024/1024 = 24M的空间就够了。

(3)接下来我们看看bitmap在redis上的应用

假设这是我们uid的登录情况
898xxx代表uid ,0代表未登录,1代表登录

Monday
8987129 0
8298191 1
8892198 1

Tuesday
8987129 0
8298191 0
8892198 1

Wednesday
8987129 0
8298191 1
8892198 1

Thursday
8987129 0
8298191 0
8892198 0

Friday
8987129 0
8298191 1
8892198 1

Saturday
8987129 0
8298191 1
8892198 0

Sunday
8987129 1
8298191 1
8892198 0

用setbit方法,将这些数据录入到redis中:

setbit key offset value
设置offset对应二进制上的值,返回该位上的旧值

注意:如果offset过大,则会在中间填充0
   offset最大到2^32-1,即可推出最大的字符串为512M

十六、Redis三种特殊类型之三Bitmap

接下来要计算7天内有登录行为的用户,只需要将周一到周天的值做位或运算就可以了。

位运算符

按位与运算符(&)
参加运算的两个数据,按二进制位进行“与”运算。
运算规则:0&0=0;  0&1=0;   1&0=0;    1&1=1;
    即:两位同时为“1”,结果才为“1”,否则为0
      
按位或运算符(|)
参加运算的两个对象,按二进制位进行“或”运算。
运算规则:0|0=0;  0|1=1;  1|0=1;   1|1=1;
    即 :参加运算的两个对象只要有一个为1,其值为1。
     
异或运算符(^)
参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:0^0=0;  0^1=1;  1^0=1;   1^1=0;
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。

最后计算7天内登录过的活跃用户为2:

十六、Redis三种特殊类型之三Bitmap

 

-------------------------------------------------------------------------------------------

还有很多优势,比如通过 bitcount可以很快速的统计,比传统的关系型数据库效率高很多。

1、比如统计年活跃用户数量

2、比如统计三天内活跃用户数量

3、连续三天访问的用户数量 

4、三天内没有访问的用户数量 

5、统计在线人数 

6、bitmap的优势,以统计活跃用户为例

每个用户id占用空间为1bit,消耗内存非常少,存储1亿用户量只需要12.5M

7、bitmap - Redis布隆过滤器 (应对缓存穿透问题)

举例:比如爬虫服务器在爬取电商网站的商品信息时,首先经过缓存,如果缓存查不到,再去数据库获取信息,因为爬虫的效率很高,且sku很有可能是不存在或者已下架的,就会造成缓存穿透,大量请求被发送到数据库,导致服务器受到影响。

此时,可以在缓存层之前,添加一个布隆过滤器,布隆 过滤器看作是一个bitmap,sku作为offset值,如果商品真实存在,bit值设为1。首先将商品数据初始化,当有请求时,通过getbit判断sku是否有效。如果布隆过滤器认为商品不存在,就拒绝访问,这样就可以保护存储层。