布隆过滤器

了解

  1. 1970年,由Bloom 提出的
  2. 二进向量(位数组)随机映射函数组成映射关系
  3. BitSet 作为位容器(位数组),位值为 0 或者 1
  4. 随机映射函数(HashFunction),目的生成一个对应的数值,然后存入到 BitSet中。
  5. 优点:
    1. 数组容器占用空间小。存储100W个数据,占 122 KB(100W*1bit/8/1024)
    2. 如果存储过数据,容器必定有痕迹
  6. 缺点:
    1. 容器有痕迹的,不一定存储过数据
    2. 容器中只能添加数据,不能删除数据。删除元素导致位值的变化,会提高容器的错误率
    3. 数据量越大,错误率会越高。所以一般 位容器的初始位数会很大

注 : 1 M = 1024 KB = 1024 * 1024 B(byte) = 1024 * 1024 * 8 bit

原理

image

添加对象

被添加的对象,通过 HashFunction 会生产一个或多个对应的数值,并存储到 BitSet 容器中。BitSet 中位值会由 0->1 ,多个对象存储时,位值也只会存储到 1。

判断对象

判断的过程中,实际是看对象通过 HashFunction 所生产的一个或多个对应的数值,在 BitSet 中的位值是否都为1
图中,对象4 对应的所有位值里面,存在一个值为 0,则 对象4 一定没存储过。

实现

/**
 * 布隆过滤器
 * 
 * @author ZZ_C
 *
 */
public class TestBloomFilter {

	/**
	 * 一个长度为10亿的位
	 */
	private static int DEFAULT_SIZE = 256 << 22;

	/**
	 * 为了降低错误率,使用hash算法,定义8个不同的质数
	 */
	private static final int[] seeds = { 3, 5, 7, 11, 13, 17, 19, 23 };

	/**
	 * 定义seeds.length 个 hash 函数用于哈希计算
	 */
	private static HashFunction[] functions = new HashFunction[seeds.length];

	/**
	 * 布隆过滤器的核心容器,初始化,定义了 10 亿个位
	 */
	private static BitSet bitSet = new BitSet(DEFAULT_SIZE);

	TestBloomFilter() {

		for (int i = 0; i < seeds.length; i++) {
			functions[i] = new HashFunction(DEFAULT_SIZE, seeds[i]);
		}
	}

	public void add(String value) {

		for (HashFunction function : functions) {

			bitSet.set(function.toHash(value));
		}
	}

	public boolean contains(String value) {

		for (HashFunction function : functions) {
			// 只要有一个不存在,则 value 一定不存在
			boolean result = bitSet.get(function.toHash(value));
			if (Objects.equals(result, false)) {
				return false;
			}
		}

		return true;
	}

	public static void main(String[] args) {

		TestBloomFilter bloomFilter = new TestBloomFilter();
		bloomFilter.add("斗罗大陆");
		bloomFilter.add("辰东");
		System.out.println(bloomFilter.contains("我爱吃西红柿"));   // false
		System.out.println(bloomFilter.contains("辰东"));  // true
	}

	/**
	 * 作用就是算 hash值
	 * 静态类,便于处理
	 * @author ZZ_C
	 *
	 */
	static class HashFunction {

		private int size;

		private int seed;

		public HashFunction(int size, int seed) {
			super();
			this.size = size;
			this.seed = seed;
		}

		public int toHash(String value) {
			int h;
			return (value == null) ? 0 : Math.abs(seed * (size - 1) & ((h = value.hashCode()) ^ (h >>> 16)));

		}

	}
}

应用场景

Bloom Filter 在使用场景上分为两种,一种是单体架构,一个是分布式架构。
单体架构:Gava,redis
分布式架构: redis bloom(需要自行安装到 redis 中)

缓存击穿

使用布隆过滤器,将全部的有效数据都走一遍布隆过滤器,这样新来请求时,先走一遍请求的元素是否能通过布隆过滤器的验证,若未通过就直接放弃请求,若通过再执行查询

去重

用户浏览记录存入数据库时,会在Filter上通过key的hash算法存储判断其是否存在,类似于数据存在数据库中,判断该数据是否存在的信息即元数据存放在BloomFilter中,避免了每次判断数据是否存在都要去数据库exist一遍;这样推送新闻时通过布隆过滤器判断,推送内容是否已经存在,如果存在则不推送,如果不存在则推送;

posted @ 2022-05-13 19:09  之士咖啡  阅读(64)  评论(0编辑  收藏  举报