redis实现布隆过滤器

package com.jmdf.world;

import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.Charset;
@Component
public class BloomFilter {
        @Resource
        private RedisConfig redisConfig;
        //预计插入量
        private long expectedInsertions = 1000;
        //可接受的错误率
        private double fpp = 0.001F;
        //布隆过滤器的键在Redis中的前缀 利用它可以统计过滤器对Redis的使用情况
        private String redisKeyPrefix = "bf:";
        private Jedis jedis;

        //利用该初始化方法从Redis连接池中获取一个Redis链接
        @PostConstruct
        public void init(){
            this.jedis = redisConfig.getRedisUtil().getJedis();
        }

        public void setExpectedInsertions(long expectedInsertions) {
            this.expectedInsertions = expectedInsertions;
        }

        public void setFpp(double fpp) {
            this.fpp = fpp;
        }

        public void setRedisKeyPrefix(String redisKeyPrefix) {
            this.redisKeyPrefix = redisKeyPrefix;
        }

        //bit数组长度
        private long numBits = optimalNumOfBits(expectedInsertions, fpp);
        //hash函数数量
        private int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);

        //计算hash函数个数 方法来自guava
        private int optimalNumOfHashFunctions(long n, long m) {
            return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
        }

        //计算bit数组长度 方法来自guava
        private long optimalNumOfBits(long n, double p) {
            if (p == 0) {
                p = Double.MIN_VALUE;
            }
            return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
        }

        /**
         * 判断keys是否存在于集合where中
         */
        public boolean isExist(String where, String key) {
            long[] indexs = getIndexs(key);
            boolean result;
            //这里使用了Redis管道来降低过滤器运行当中访问Redis次数 降低Redis并发量
            Pipeline pipeline = jedis.pipelined();
            try {
                for (long index : indexs) {
                    pipeline.getbit(getRedisKey(where), index);
                }
                result = !pipeline.syncAndReturnAll().contains(false);
            } finally {
                try {
                    pipeline.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (!result) {
                put(where, key);
            }
            return result;
        }

        /**
         * 将key存入redis bitmap
         */
        private void put(String where, String key) {
            long[] indexs = getIndexs(key);
            //这里使用了Redis管道来降低过滤器运行当中访问Redis次数 降低Redis并发量
            Pipeline pipeline = jedis.pipelined();
            try {
                for (long index : indexs) {
                    pipeline.setbit(getRedisKey(where), index, true);
                }
                pipeline.sync();
            } finally {
                try {
                    pipeline.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * 根据key获取bitmap下标 方法来自guava
         */
        private long[] getIndexs(String key) {
            long hash1 = hash(key);
            long hash2 = hash1 >>> 16;
            long[] result = new long[numHashFunctions];
            for (int i = 0; i < numHashFunctions; i++) {
                long combinedHash = hash1 + i * hash2;
                if (combinedHash < 0) {
                    combinedHash = ~combinedHash;
                }
                result[i] = combinedHash % numBits;
            }
            return result;
        }

        /**
         * 获取一个hash值 方法来自guava
         */
        private long hash(String key) {
            Charset charset = Charset.forName("UTF-8");
            return Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asLong();
        }

        private String getRedisKey(String where) {
            return redisKeyPrefix + where;
        }
    }

实现原理:
where 是用来存放bigbit的一个二进制集合
key 查询的目标key
数据放入阶段:
根据要放入的数据量、误差率:预估~
计算所需要的bit数组的位数,where是bit容器的键名
在这里插入图片描述
所有的数据都存储在这个bit集合里,判断是否存在
是根据目标key的hash后分配在bit集合的坐标,如果这个坐标存在,则这条数据存在。
一旦存入成千上万个位数据到Redis后,千万不要期待使用循环的方式一一清空bitmap的数据,最简单那就是给bitmap的key设置过期时间,只要执行

EXPIRE "bitmap的key值" 0

那么这个bitmap的所有已设置的位就能都清空了!!执行时间不超过1秒。这算是过期时间的一个妙用吧,以前一直都只是简单的当成过期时间而已,没想到在删除数据方面非常高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值