缓存经典的三种异常场景
| 场景 | 描述 | 解决方案 |
|---|---|---|
| 缓存穿透 | 数据库根本没有数据,所以缓存也没有。大量无效 key 全部涌入数据库查询 | 布隆过滤器 |
| 缓存击穿 | 缓存失效瞬间并发打到数据库,也就是很多都是无效查询。 | 在 redis 和数据库之间加分布式锁,限制只有一个会去查成功。 |
| 缓存雪崩 | 大量 key 同时过期 | 解决:差异化缓存时间,不要让那么多的 key 同时失效。 |
一、角色
初始时过滤器的位数组内容全都为 0:

put 元素进去后,这个元素经过不同的 hash 函数计算出对应的下标,命中的置为 1:


只有全为 1 的元素才被允许继续,有为 0 的那就不用再走后面的流程了。
二、为什么要有这个东西
可以防止大量的无效 key 过来。
布隆过滤器只能判定一个元素不在集合里面,不能判断一定存在。
如果不存在记录的,那就是肯定不存在。
如果存在记录,这个情况,并不能确认是否是这个 key 进入所记录的。
因为可能会被其他 key 的记录给覆盖,后来的可能使用之前的覆盖记录了,这会导致误判。
三、框架实现
guava 中com.google.common.hash.BloomFilterStrategies的实现。
四、删除操作
上面的过滤器是不支持 key 的删除的,也就是说一旦 key 经过了过滤器,也就“污染”了过滤器,这种污染是不可逆的。
那如果我想要删除 ‘apple’ 这个 key,该怎么办呢? 一个方法是为每个位进行计数。
例如上面,插入 apple 后
array[2] = 1
array[5] = 1
array[8] = 1
插入 banana 后
array[1] = 1
+array[5] = 2 // 因为之前已经是1了,这里再次命中,就再加1
array[9] = 1
这个时候我们删除 banana,就把对应 key 的位计数各自 -1 即可。
但是这个时候又有新的问题,例如过滤器中从来都没有进入过 banana,此时直接去删除 banana,不就会把其他 key 的位计数给删掉了吗?