1.1.思路分析
当我们做项目的时候肯定遇到过签到功能吧,如果用平常数据库,假如一个用户1年签到100次,而网站有100万用户,就会产生1亿条记录。
随着用户量增多、时间的推移,这张表中的数据只会越来越多,占用的空间也会越来越大。
有没有什么办法能够减少签到的数据记录,减少空间占用呢?
大家回忆一下,小时候上补习班时的签到卡:
在这张小小的卡片上面,就记录了从一个月的第一天到最后一天的所有的签到情况。诶,你今天来上课了那就勾一下,没来就空着。这样呢,通过一个小小的卡片就能够记录一个同学这一个月的签到的情况了。虽然很原始,但是非常高效。
如果我们能用程序来模拟这样的签到卡,用一行数据去记录一个用户一个月的签到情况,可想而知,那比这种数据库的方式是不是要大大地节省空间。
那么问题来了,我们该怎么样去模拟这样的一个签到卡?
其实并不复杂,你想嘛?一个用户签到的情况无非就两种,要么签了,要么没签。这就像我们电路当中的这个二极管,那要么通电,要么不通,那用程序怎么表示这种状态呢?
没错,就是 0 或者1
如果我们按月来统计用户签到信息,签到记录为1,未签到则记录为0,就可以用一个长度为31位的二级制数来表示一个用户一个月的签到情况
我们知道二进制是计算机底层最基础的存储方式了,其中的每一位数字就是计算机信息量的最小单位了,称之为bit,一个月最多也就 31 天,因此一个月的签到记录最多也就使用 31 bit 就能保存了,还不到 4 个字节。
而如果用到我们前面讲的数据库方式来保存相同数据,则要使用数百字节,是这种方式的上百倍都不止。
可见,这种用二进制位保存签到记录的方式,是不是非常高效啊!
像这种把每一个二进制位,与某些业务数据一一映射(本例中是与一个月的每一天映射),然后用二进制位上的数字0和1来标识业务状态的思路,称为位图。也叫做BitMap.
这种数据统计的方式非常节省空间,因此经常用来做各种数据统计。比如大名鼎鼎的布隆过滤器就是基于BitMap来实现的。
OK,那么利用BitMap我们就能直接实现签到功能,并且非常节省内存,还很高效。所以就无需通过数据库来操作了。
那么BitMap该怎么使用呢?
1.2.BitMap用法
签到
如果要签到就可以利用上面的这个命令,例如这个月的第1、2、3、6、7、8几天签到了,就可以这样
# 第1天签到
SETBIT bm 0 1
# 第2天签到
SETBIT bm 1 1
# 第3天签到
SETBIT bm 2 1
# 第6天签到
SETBIT bm 5 1
# 第7天签到
SETBIT bm 6 1
# 第8天签到
SETBIT bm 7 1
最终Redis中保存的效果
查询
那如果我们要查询签到记录怎么办?
那就是要读取BitMap中的数据,可以用这个命令
BITFIELD key GET encoding offset
-
key:就是BitMap的key
-
GET:代表查询
-
encoding:返回结果的编码方式,BitMap中是二进制保存,而返回结果会转为10进制,但需要一个转换规则,也就是这里的编码方式
-
u:无符号整数,例如 u2,代表读2个bit位,转为无符号整数返回
-
i:又符号整数,例如 i2,代表读2个bit位,转为有符号整数返回
-
-
offset:从第几个bit位开始读取,例如0:代表从第一个bit位开始
例如,我想查询从第1天到第3天的签到记录,可以这样:
BITFIELD bm GET u3 0
拓展
Redis最基础的数据类型只有5种:String、List、Set、SortedSet、Hash,其它特殊数据结构大多都是基于以上5这种数据类型。
BitMap也不例外,它是基于String结构的。因为Redis的String类型底层是SDS,也会存在一个字节数组用来保存数据。而Redis就提供了几个按位操作这个数组中数据的命令,实现了BitMap效果。
由于String类型的最大空间是512MB,也就是2的31次幂个bit,因此可以保存的数据量级是十分恐怖的。