Bitset
https://blog.csdn.net/hgd613/article/details/54095729
https://www.jianshu.com/p/bcded5fdc4c2
Redis用bitset(bitmap)来统计日活跃量
假设这样一个场景,假如每个网站有1亿的用户,那么我们怎么来统计这个网站的日登陆数或者说有哪些用户登录过这个网站。
最常见的做法就是设计一张用户登录表:
user_login:
user_uid login_date
0 2017-7-1
1 2017-7-1
0 2017-7-2
如果平均一个人一天登录1次,那么1亿个用户一个星期就会产生1 * 1 * 7 = 7亿条数据,一个月就会产生30亿条数据,这对数据库的压力是很大的,只是统计一下用户登录,没必要花费这么多的资源。
这个时候我们就可以用reids 的bitmap来解决。
用户是否登录可以用0/1来表示,0代表用户不登陆,1表示登录,那么1bit 就可以表示用户是否登录。
1亿个用户一天的数据量也就 1 0000 0000bit = 11.92m,也就是说用户一天的登录信息也就产生11.92m的数据量。一个月也就357.63m的数据量。
具体实现过程(为了实验方便,我们就假设4个用户0,1,2,3,统计两天的登录量):
mon: 1010 (用户0未登录,用户1登录,用户2未登录,用户3登录)
tue: 1101 (用户0登录,用户1未登录,用户2登录,用户3登录)
初始化数据:
127.0.0.1:6379> setbit mon 0 0
(integer) 1
127.0.0.1:6379> setbit mon 1 1
(integer) 1
127.0.0.1:6379> setbit mon 2 0
(integer) 0
127.0.0.1:6379> setbit mon 3 1
(integer) 0
127.0.0.1:6379> setbit tue 0 1
(integer) 1
127.0.0.1:6379> setbit tue 1 0
(integer) 1
127.0.0.1:6379> setbit tue 3 1
(integer) 0
127.0.0.1:6379> setbit tue 4 1
(integer) 1
如果要统计这两天都登陆的用户:
127.0.0.1:6379> bitop AND result mon tue
(integer) 1
127.0.0.1:6379> getbit result 0
(integer) 0
127.0.0.1:6379> getbit result 1
(integer) 0
127.0.0.1:6379> getbit result 2
(integer) 0
127.0.0.1:6379> getbit result 3
(integer) 1
可以看到mon 和 tue做and运算,得到结果result为:0001,则表示用户3连续两天都登陆,其他用户两天中只有一天登录。
基于bitset实现手机号的黑白名单方案
目前很多应用都把手机号码作为登录的账户名,本文介绍一种高效的基于手机号,来实现黑白名单的方案。
在这里我先用一个例子来说明位图。
假设我有一个0到31的集合,集合里面的元素不重复,比如这样{0,3,1,5,2,19,7,8,31,21,10}。通过位图,我可以将这样的集合表示为11110001101000000001010000000001, 其中1表示该数值为下标的数存在在集合中,比如第一个1表示0存在集合中,第二个1表示1存在集合中,等等。
通过这样做,我们起码可以得到两个好处
节省空间--我们可以用二进制一个位来存储存储两个信息,一是存不存在,而是存在的数是多少(通过一个bit就可以得到这么多信息,真了不起)。
排序--从左到右遍历这个为图,我们可以得到排序的集合,比如上例中,我们可以得到集合 {0,1,2,3,7,8,10,19,21,31}
如果将所有这电话号码用位图表示,那么需要9999999999个bit (10个9, 考虑到手机号码的第一位都是1)。 9999999999 bit = (9999999999/8) byte = (9999999999 / (8 * 1024)) KB = (9999999999 / 8 10241024) M = 1192M
嗯....,1192M,内存占有还是太大,我们前期的黑白名单所占内存远远低于这个值。 考虑到手机号码的前3位都差不多,而且总数最多30个,所以考虑到手机号码拆分为两部分(头3位和剩余8位),这样就把位图的位数就降2个数量级了。所以如果我们要在内存中装入手机号码后8位,需要99999999(8个9)个bit,算一下:99999999 bit = (99999999/8) byte = (99999999 / (8 * 1024)) KB = (99999999 / 8 10241024) M = 11.92M
内存中装入头2位(手机号第一位始终为1,因此只需装入后面2位),需要99(2个9)个bit,算下
99 bit = 99/8 byte =12.375 byte。
大约12M 内存,就能表示所有手机号码,达到黑白名单。
这个比用redis等缓存,高效很多,java里面的bitset也是这个原理。
Tags : 本文未设置标签
您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。
本站大部分内容收集于互联网,如果有侵权内容、不妥之处,请联系删除。敬请谅解!