缓存穿透
查询数据库中不存在记录,这样就绕过缓存,直接查询数据库,造成数据库压力巨大。例如:一个博客系统,通过id查询相应文章内容。攻击者随机生成文章id,然后大量访问该系统,这样每次请求都能直接查库。
解决方案:
1,空值也放入到缓存中
2,使用布隆过滤器,请求进来,先查询布隆过滤器,布隆过滤器不存在则放入缓存然后返回
缓存击穿
缓存中没有值,而数据库有值,直接去查询数据库,一般发生在缓存正好失效,恰好此时有大量请求进来。
缓存雪崩
短时间内,缓存内大量数据失效,造成大量请求直接查库。
解决方案
1,controller上就加缓存,返回明确的结果对象;数据库查询不到数据,就将空值或默认值放入到缓存中
2,使用布隆过滤器,请求进来,先查询布隆过滤器,布隆过滤器不存在则放入缓存然后返回。系统启动时,将要查询的条件数据放入到布隆过滤器,查询条件数据增加时,同步增加到布隆过滤器
3,加锁,确保单线程查询数据库
4,多级缓存,防止缓存系统宕机
//分布式系统的话,可以使用分布式锁
public volatile static HashMap<String, String> m = new HashMap<>();
//布隆过滤器预计存放的数据量:100000000 误报率<0.0001
public volatile static BloomFilter<CharSequence> bloomFilter =
BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 100000000, 0.0001);
public Student query(String id) {
Student student = null;
//布隆过滤器,具体是否使用及使用方式依据项目实际情况而定
if (!bloomFilter.mightContain(id)) {
//放入redis和本地缓存,具体如何实现,依据项目实际情况而定
return student;
}
if (Strings.isNullOrEmpty(m.get(id))) {
synchronized (this) {
//再次判断,防止首次判空后加锁前有线程更新了m的值
if (Strings.isNullOrEmpty(m.get(id))) {
m.put(id, id);
student = studentDao.query(id);
//放入redis和本地缓存,具体如何实现,依据项目实际情况而定
}
}
}
return student;
}
布隆过滤器
布隆过滤器是一个快速判断大量数据内是否存在指定数据的数据结构。布隆过滤器判断不存在,则一定不存在,判断存在,是大概率存在,并不一定存在,只是大概率存在。主要应用在网站黑名单拦截,垃圾邮件拦截等等
布隆过滤器本质上是一个字节数组。
添加:数据经过N次hash计算,将数组内这N次hash值对应的位置置为1
查询:将查询数据经过N次hash计算,得到N个hash值,判断数组内,这N个位置是否都是1,不全是1说明查询值一定不存在,全是1,大概率存在。
例如:有字节数组a,添加字符串“aaa”,将“aaa”经过3次hash计算,得到3个hash值,1,3,5,然后将a[1]=1,a[3]=1,a[5]=1;查询字符串“bbb”是否存在,将“bbb”经过3次hash计算,得到3个hash值,2,4,6,然后判断数组2,4,6位置数据是否全是1