BloomFilter(布隆过滤器)结合redis使用---可解决集群数据问题~

前言

Bloom Filter是一个占用空间很小、效率很高的随机数据结构,它由一个bit数组和一组Hash算法构成。可用于判断一个元素是否在一个集合中,查询效率很高(1-N,最优能逼近于1)。

布隆过滤器概念详解可参考:https://blog.csdn.net/jiaomeng/article/details/1495500

常见使用场景

1、缓存击穿

将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉

比如说:活动详情接口,我们可以缓存所有活动id,每一次调用活动详情接口时先通过布隆过滤器进行一次判断,通过后在查询活动详情的缓存。否则直接将此请求拦截。比如说:活动详情接口,我们可以缓存所有活动id,每一次调用活动详情接口时先通过布隆过滤器进行一次判断,通过后在查询活动详情的缓存。否则直接将此请求拦截。

2、网页爬虫对URL的去重,避免爬取相同的URL地址

3、垃圾邮件地址过滤

优缺点

优点

  1. 全量存储单不存储数据本身(数据通过k个hash算法计算出来的),对于数据保密有优势
  2. 空间高效率
  3. 插入/查询 的时间是常数O(k)

缺点

  1. 存在误算率,数据越大,误算越高
  2. 不能从bloomfilter中删除数据,因为不能判断是否真正存在
  3. 数组长度和hash 个数确认复杂

使用

第一步跑不开是引用maven包~。

    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>${guava.version}</version>
    </dependency>

然后定义我们的布隆过滤器

public class BloomFilterHelper<T> {

    private int numHashFunctions;

    private int bitSize;

    private Funnel<T> funnel;

    public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) {
        Preconditions.checkArgument(funnel != null, "funnel不能为空");
        this.funnel = funnel;
        // 计算bit数组长度
        bitSize = optimalNumOfBits(expectedInsertions, fpp);
        // 计算hash方法执行次数
        numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
    }

    public int[] murmurHashOffset(T value) {
        int[] offset = new int[numHashFunctions];

        long hash64 = Hashing.murmur3_128().hashObject(value, funnel).asLong();
        int hash1 = (int) hash64;
        int hash2 = (int) (hash64 >>> 32);
        for (int i = 1; i <= numHashFunctions; i++) {
            int nextHash = hash1 + i * hash2;
            if (nextHash < 0) {
                nextHash = ~nextHash;
            }
            offset[i - 1] = nextHash % bitSize;
        }

        return offset;
    }

    /**
     * 计算bit数组长度
     */
    private int optimalNumOfBits(long n, double p) {
        if (p == 0) {
            // 设定最小期望长度
            p = Double.MIN_VALUE;
        }
        return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    }

    /**
     * 计算hash方法执行次数
     */
    private int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
    }

}

定义redisService 结合redis使用

public class BloomRedisService<T> {

    private RedisTemplate<String, String> redisTemplate;

    private BloomFilterHelper<T> bloomFilterHelper;

    public void setBloomFilterHelper(BloomFilterHelper<T> bloomFilterHelper) {
        this.bloomFilterHelper = bloomFilterHelper;
    }

    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 根据给定的布隆过滤器添加值
     */
    public void addByBloomFilter(String key, T value) {
        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
        int[] offset = bloomFilterHelper.murmurHashOffset(value);
        for (int i : offset) {
            redisTemplate.opsForValue().setBit(key, i, true);
        }
    }

    /**
     * 根据给定的布隆过滤器判断值是否存在
     */
    public boolean includeByBloomFilter(String key, T value) {
        Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
        int[] offset = bloomFilterHelper.murmurHashOffset(value);
        for (int i : offset) {
            if (!redisTemplate.opsForValue().getBit(key, i)) {
                return false;
            }
        }
        return true;
    }

}

接下来将其塞入到springboot中让ioc管理就可以愉快到使用啦

@Configuration
public class BloomFilterConfig {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Bean
    public BloomFilterHelper<String> initBloomFilterHelper() {
        return new BloomFilterHelper<>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8)
                .putString(from, Charsets.UTF_8), 1000000, 0.01);
    }

    /**
     * 布隆过滤器bean注入
     * @return
     */
    @Bean
    public BloomRedisService<String> bloomRedisService(){
        BloomRedisService<String> bloomRedisService = new BloomRedisService<>();
        bloomRedisService.setBloomFilterHelper(initBloomFilterHelper());
        bloomRedisService.setRedisTemplate(redisTemplate);
        return bloomRedisService;
    }

}

使用方式:

初始化布隆过滤器

@Autowired
private BloomRedisService<String> bloomRedisService;

塞入数据, 多条数据循环塞入即可.

bloomRedisService.addByBloomFilter("test","123");

判断数据是否存在.

if (!bloomRedisService.includeByBloomFilter("test","321")){
	return;
}

注意KEY需要一致噢~

文章到此结束啦,希望对你有帮助

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值