Bloom filter
以一种简单的算法,高效的验证数据是否存在。
假如我们有一个需求:
当用户点击某一个的商品时,前台会将该商品的 ID 传给后台请求数据,后台根据 ID 将数据响应给前台,前台接收数据后进行渲染。
public Object getData(String key) {
Object data = null;
//数据在redis中不存在
if (!redisTemplate.hasKey(key)) {
//去数据库查,如果查到数据,则存放到redis中,并return数据
//如果没查到数据,则直接返回null
}
data = redisTemplate.opsForValue().get(key);
return data;
}
上面代码是一种很常见的写法,但是它存在问题:
如果缓存中不存在该数据,则会去数据库中拉取数据。当攻击者发现数据请求链接时,就会发送一个不存在的 ID 进行恶意攻击,因为redis中不存在该数据,系统将会去数据库中查询数据,如果请求并发量很高,可能会造成 数据库崩溃!

那么我们可以将代码进行改进:
public Object getData(String key) {
Object data = null;
//数据在redis中不存在
if (!redisTemplate.hasKey(key)) {
//去数据库查,如果查到数据,则存放到redis中,并return数据
//如果没查到数据,也存放到redis中,并return null
}
data = redisTemplate.opsForValue().get(key);
return data;
}
如果没有查到数据的话,也将 ID 存到redis中,那么下一次恶意攻击的时候会直接返回redis中的数据,不会去数据库中查询数据了。
这就结束了吗?没有这么简单!
如果攻击者每次都发送随机的 ID,那要怎么办呢?
使用 Bloom filter(布隆过滤器)
什么是布隆过滤器?
本质上布隆过滤器是一种数据结构,特点是高效地插入和查询,占用空间更少。根据查询结果可以用来告诉你某样东西 一定不存在 或者 可能存在。
初始化一个布隆过滤器:

在布隆过滤器中插入两个值 X 和 Y
(多次hash运算后,算出每次所在的下标,并将下标的值改成 1)

验证 Z 是否存在:
(多次hash运算后,算出每次所在的下标,如果下标全部为 1 则该数据可能存在,如果有一个下标为 0 则该数据一定不存在)

场景:
我们可以在程序刚运行的时候,读取数据库中的全部 ID,并将数据插入到布隆过滤器中。
那么在查询数据的时候,可以先通过布隆过滤器来判断值是否存在(可能会误判)
如果存在就可以获取数据,如果不存在就返回 null,保证了程序的安全。