它实际上是一个很长的二进制数组+一系列随机hash算法映射函数,主要用于快速的判断一个元素是否在集合中.
他的判断结果并不是百分之百准确的
因为可能发生hash冲突
把已经存在数据的key存放到布隆过滤器中,相当于在redis前面再加一层拦截过滤.
当有新的请求时,先到布隆过滤器中查询是否存在,如果布隆过滤器不存在该条数据则直接返回,如果布隆过滤器中已存在,才去查询redis缓存,如果redis没有再去查询mysql
直接判断值在不在布隆过滤器里面
public static void main(String[] args) {System.out.println("Aa".hashCode());System.out.println("BB".hashCode());}
使用多个hash函数对key进行hash运算得到一个整数索引值,对位数组长度进行取模运算得到一个位置,每个hash函数都会得到一个不同的位置,将这几个位置都置为1就完成了add操作
添加key的图示
只要有其中一位是0就表示这个key不存在,如果都是1(都是布隆过滤器就返回存在),但是不一定就代表存在这对应的key.
因为只要是hash函数就存在hash冲突,哪怕是采用了多个hash函数也有可能会跟其他的多个key值hash出来的值冲突,所以不能确定一定存在
查询误判示意图:
redis主机挂了,redis全盘崩溃(或者缓存中有大量数据同时过期)
一般情况下,先查询缓存redis是否有该条数据,缓存未命中时,在查询数据库.当数据库也不存在该条数据时,每次查询都要访问数据库,这就是缓存穿透.它带来的问题就是当有大量的请求查询数据库本身就不存在的数据时,就会给数据库带来压力,甚至是直接拖垮数据库
就是缓存一个空值或者业务上定义的缺省值在redis里面缓存返回(每次换id就不好用了 而且会导致大量的无用key堆积)
public static final int _1w = 10000;public static final int size = 100 * _1w;public static double fpp = 0.03;/*** 入门demo*/public void bloomFilter() {BloomFilter filter = BloomFilter.create(Funnels.integerFunnel(), 100);System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));filter.put(1);filter.put(2);System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));}/*** 误判率演示和源码分析*/public void bloomFilter2() {BloomFilter filter = BloomFilter.create(Funnels.integerFunnel(), size);for (int i = 0; i < size; i++) {filter.put(i);}ArrayList
当你的误判率fpp越低,需要占用的bit数组就越长,对值进行hash计算的hash函数就越多,这样的话能更加避免hash冲突的情况发生,用空间和时间来换取准确率,guava默认设置是0.03,个人用的话最低也就建议到0.01,再低的话导致程序变慢就得不偿失了.
public static final int _1w = 10000;public static final int size = 100 * _1w;public static double fpp = 0.01;static RedissonClient redissonClient;static RBloomFilter rBloomFilter;static {Config config = new Config();config.useSingleServer().setAddress("redis://8.131.64.231:16678").setDatabase(0).setPassword("liang#0601");redissonClient = Redisson.create(config);rBloomFilter = redissonClient.getBloomFilter("phoneList", new StringCodec());rBloomFilter.tryInit(size, fpp);rBloomFilter.add("10086");redissonClient.getBucket("10086", new StringCodec()).set("chinamobile10086");}public static String getPhone(String id) {String result;if (rBloomFilter.contains(id)) {RBucket rBucket = redissonClient.getBucket(id, new StringCodec());result = rBucket.get();if (result != null) {return "result form redis:" + result;} else {result = getByMysql(id);if (result == null) {return null;}redissonClient.getBucket(id, new StringCodec()).set(result);}return "result form mysql:" + result;}return null;}public static String getByMysql(String id) {return "10086mysql";}public static void main(String[] args) {String phone = getPhone("10086");System.out.println(phone);redissonClient.shutdown();}
默认的误判率是0.01 默认的bit数组是100
热点key突然失效了,导致大量的请求直接打到了MySQL上面
两个主从缓存差异化失效时间,在你删除缓存的时候就不会缓存击穿