条件
缓存穿透是因为查询redis时发现其中没有要查询的数据,大量请求DB导致DB压力过大崩溃。这种情况大部分是因为恶意攻击。
解决方法
使用布隆过滤器
它是一种基于概率的数据结构,主要用来判断某个元素是否在集合内,它具有运行速度快(时间效率),占用内存小的优点(空间效率),但是有一定的误识别率和删除困难的问题 。它能够告诉你某个元素一定不在集合内 或可能在集合内。
原理总结
1.一个很长的二进制数组 (位数组,就是这个数组里只有0和1)
2.若干个哈希函数
3.空间效率和查询效率高
4.不存在漏报(False Negative),即某个元素在某个集合中,肯定能报出来。
5.可能存在误报(False Positive),即某个元素不在某个集合中,可能也被爆出来。
5.删除困难
代码实现布隆过滤器
java
public class SimpleBloomFilter {
private static final int DEFAULT_SIZE = 2 << 24; // 布隆过滤器的比特长度
private static final int[] seeds = new int[] {7, 11, 13, 31,37, 61}; // 这里要选取质数,能很好的降低错误率
private BitSet bits = new BitSet(DEFAULT_SIZE);
private SimpleHash[] func = new SimpleHash[seeds.length];
public static void main(String[] args) {
String value = "crankzcool@gmail.com";
SimpleBloomFilter filter = new SimpleBloomFilter();
System.out.println(filter.contains(value));
filter.add(value);
System.out.println(filter.contains(value));
}
public SimpleBloomFilter() {
for (int i = 0; i < seeds.length; i++) {
func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
}
}
public void add(String value) {
for (SimpleHash f: func) {
bits.set(f.hash(value), true);
}
}
public boolean contains(String value) {
if (value == null) {
return false;
}
boolean ret = true;
for (SimpleHash f : func) {
ret = ret && bits.get(f.hash(value));
}
return ret;
}
public static class SimpleHash {
private int cap;
private int seed;
public SimpleHash(int cap, int seed) {
this.cap = cap;
this.seed = seed;
}
public int hash(String value) {
int result = 0;
int len = value.length();
for (int i = 0; i < len; i++) {
result = seed * result + value.charAt(i);
}
return (cap - 1) & result;
}
}
pom引入依赖实现布隆过滤器
pom依赖
XML
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0.1-jre</version>
</dependency>
代码实现
java
public static void main(String[] args) {
// 1.创建符合条件的布隆过滤器
// 预期数据量10000,错误率0.0001
BloomFilter<CharSequence> bloomFilter =
BloomFilter.create(Funnels.stringFunnel(
Charset.forName("utf-8")),10000, 0.0001);
// 2.将一部分数据添加进去
for (int i = 0; i < 5000; i++) {
bloomFilter.put("" + i);
}
System.out.println("数据写入完毕");
// 3.测试结果
for (int i = 0; i < 10000; i++) {
if (bloomFilter.mightContain("" + i)) {
System.out.println(i + "存在");
} else {
System.out.println(i + "不存在");
}
}
}