赞
踩
BloomFilter过滤器可以快速判断某值是否存在,虽然不一定准确,但是相对而且它既快速而且又相对准确。
Bloom Filter没有False Negative,即判断某元素时否在Bloom Filter中时,如果返回False, 则可以肯定该元素不在集合中,但是Bloom Filter存在False Positive, 即如果返回True, 并不能完全肯定元素就在集合中,但是有很大的概率确实在集合中。
如何根据输入元素个数n,确定位数组的大小m和哈希函数的个数k?
https://blog.csdn.net/qq_18495465/article/details/78500472
Guava中的Bloom Filter, Bloom Filter是一种省内存的基于概率的数据结构,可判断一个元素是否在集合中。
位数组和k个散列函数
1.位数组
初始状态时,BloomFilter是一个长度为m的位数组,每一位都置为0。这个结构刚和和redis的bitMap相同,当然也和Java自带的bitSet结构相似,唯一不同在于一个中间件存储数据量多,一个Java内存的数据量少。
2.添加元素(k个独立的hash函数)
添加元素时,对x使用k个哈希函数得到k个哈希值,对m取余,对应的bit位设置为1。
这里和Java的单个hash不同,它会经历2个以上不同质数hash函数,对应到不同的bit位,如上图两个个hash函数值对应的x1,x2分别对应到不同的bit位。多层hash的目的就在于避免单次hash造成的bit位相同,从而影响判断结果,因此它也无法保证结果是一定的,只能做到尽量符合预期结果。
3.判断元素是否存在
判断y是否属于这个集合,对y使用k个哈希函数得到k个哈希值,对m取余,所有对应的位置都是1,则认为y属于该集合(哈希冲突,可能存在误判),否则就认为y不属于该集合。
图中y1不是集合中的元素,y2属于这个集合或者是一个false positive。
二进制数据的bit位异或操作很容易就能判断是全1的真还是假,因此速度很快,不用像字符串判断中需要一个一个的做对比。当然问题依然存在就是如果两个不同字符串多次hash的值依然相同的问题,只要数据量足够大这样的结果仍然会出现,只是概率问题。因此BloomFilter只能做到尽量合乎预期而不能做到完全一致。
BloomFilter有以下参数:
就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。
Redis中的BitMap
Redis从2.2.0版本开始新增了setbit,getbit,bitcount等几个bitmap相关命令。虽然是新命令,但是并没有新增新的数据类型,因为setbit等命令只不过是在set上的扩展。
setbit基本操作
redis> SETBIT bit 0 1 # 0001
(integer) 0
redis> GETBIT bit 3 1 # 1001
(integer) 1
redis> GETBIT bit 100 # bit 默认被初始化为 0
(integer) 0
redis> BITCOUNT bits # bit中又多少个1
(integer) 1
BITOP operation resultKey key1 key2。operation 是位运算的操作,有 AND,OR,XOR,NOT。resultKey 是把运算结构存储在这个 key 中,key1 和 key2 是参与运算的 key,参与运算的 key 可以指定多个。
setbit的offset是用大小限制的,在0到 232(最大使用512M内存)之间,即0~4294967296之前,超过这个数会自动将offset转化为0,因此使用的时候一定要注意。
https://www.runoob.com/redis/strings-setbit.html
使用场景一:用户签到
使用场景二:统计活跃用户
使用场景三:用户在线状态
用户日活,月活,留存率的统计
统计活跃用户为例
每个用的key:"REPORT:1"对应的bit位010001中的1则对应具体某天的签到状态
总共签到天数:bitcount REPORT:1
时间段内的签到天数:获取index为[start,end]的bit位统计1的个数
判断当天是否已经签到:getbit REPORT:1 offset
/** * @program: demo * @description: redisTest * @author: Bamboo zjcjava@163.com * @create: 2019-10-13 23:07 **/ @RunWith(SpringRunner.class) @SpringBootTest(classes = DemoApplication.class) public class SpringRedisTest { @Autowired private RedisClient redisClientCache; private JedisPool jedisPool; @Test public void test() { // 用户ua的活跃时间 addCount("ua","20191011"); addCount("ua","20191012"); addCount("ua","20191013"); addCount("ua","20191014"); uniqueCount("ua","20191011"); uniqueCount("ua","20191009"); uniqueCount("ua","20191011","20191012");//两个日期内的活跃次数 totalCount("ua");//总活跃次数 } //某个用户在某天是否活跃 public void addCount(String action, String date) { Jedis jedis = redisClientCache.getJedis(); String key = "REPORT:"+action ; jedis.setbit(key.getBytes(),Long.valueOf(date),true); jedis.close(); } //判断某个用户操作在某天是否活跃 public void uniqueCount(String action, String date) { Jedis jedis = redisClientCache.getJedis(); String key = "REPORT:"+action ; System.out.println("某个用户操作在某天是否是活跃用户"+date+"--- " +jedis.getbit(key.getBytes(),Long.valueOf(date))); jedis.close(); } //下面的Java代码用来统计某个用户操作在一个指定多个日期的活跃次数。 public void uniqueCount(String action, String... dates) { Jedis jedis = redisClientCache.getJedis(); BitSet all = new BitSet(); String key = "REPORT:"+action ; BitSet users = BitSet.valueOf(jedis.get(key.getBytes())); all=users.get(Integer.valueOf(dates[0]),Integer.valueOf(dates[1])+1); jedis.close(); System.out.println("多个日期的活跃次数--- " +all.cardinality()); } //统计某个用户总共活跃次数。 public void totalCount(String action) { Jedis jedis = redisClientCache.getJedis(); String key = "REPORT:"+action ; jedis.close(); System.out.println("总活跃次数--- " +jedis.bitcount(key.getBytes())); } }
使用Redis统计UV数据-HyperLogLog
pfadd:新增
pfcount:获取总数
未完待续…进行中
https://blog.csdn.net/lglgsy456/article/details/39394961
https://blog.csdn.net/z834410038/article/details/73882000
segmentfault.com/a/1190000008188655
my.oschina.net/go4it/blog/1975878
blog.csdn.net/jinjiating/article/details/91885978
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。