缓存穿透:在这种情况下,缓存服务器找不到指定key的有效缓存信息,同时下游的数据库也查询不到对应的数据,这就造成了业务系统的无效请求全部落在了数据库上,缓存服务器完全没有起到作用。
解决方案:
1)缓存空数据,查询返回的数据为空,仍把这个结果进行缓存
优点:简单
缺点:消耗内存,可能发生不一致问题
2)布隆过滤器
优点:内存占用较少,没有多余key
缺点:实现复杂,存在误判
布隆过滤器是一种数据结构,用于快速检索一个元素是否可能存在于一个集合(bit数组)中。
它的基本原理是利用多个哈希函数,将一个元素映射成多个位,然后将这些位设置成1。当查询一个元素时,如果这些位都被设置成1,则认为元素可能存在于集合中,否则肯定不存在。
所以,布隆过滤器可以准确判断一个元素是否肯定不存在,但是因为hash冲突的原因,所以它没办法判断一个元素是否一定存在。只能判断可能存在。
误判率:如果判断元素存在,可能存在误判。跟数组长度有关,数组长度越长,误判率越低,但是内存消耗增大。数组长度越小,误判率越高,内存消耗越低。一般保证误判率在5%以内可以没满足需要。
流程:
使用步骤:
Java中可以使用第三方库来实现布隆过滤器,常见的有Google Guava库和Apache Commons库以及Redis
Guava
import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; public class BloomFilterExample { public static void main(String[] args) { // 创建布隆过滤器,预计插入100个元素,误判率为0.01 BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringsFunctor(), 10, 0.01); // 插入元素 bloomFilter.put("Hollis"); bloomFilter.put("666"); bloomFilter.put("八股文"); // 判断元素是否存在 System.out.println(bloomFilter.mightContain("Hollis")); // true System.out.println(bloomFilter.mightContain("王星星")); // false } }
Apache Commons
import org.apache.commons.lang3.StringUtils; import org.apache.commons.collections4.BloomFilter; import org.apache.commons.collections4.functors.HashTransformer; public class BloomFilterExample { public static void main(String[] args) { // 创建布隆过滤器,预测插入100个元素,误判率为0.01 BloomFilter<String> bloomFilter = new BloomFilter<>(HashFunctionIdentity.hashFunction(String::hashCode), 100, 0.01); bloomFilter.put("Hollis"); bloomFilter.put("666"); bloomFilter.put("八股文"); // 判断元素是否存在 System.out.println(bloomFilter.mightContain("Hollis")); // true System.out.println(bloomFilter.mightContain("王星星")); // falses // 清除所有已知条目 for (String key : bloomFilter.keys()) { bloomFilter.remove(key); } } }
3) Redis中可以通过Bloom模块来使用,使用Redisson可以:
//创建 Config 对象,设置单个服务器地址 Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379/"); //创建 RedissonClient 对象 RedissonClient redisson = Redisson.create(config); //获取 RBloomFilter 对象 RBloomFilter<String> bloomFilter = redisson.getBlobStore("myfilter").getOrInit(new StringSerializer<>()); //初始化布隆过滤器,参数分别为容量(max elements)和误识别率(false positive rate) bloomFilter.tryInit(100, 0.01); bloomFilter.add("Hollis"); bloomFilter.add("666"); bloomFilter.add("八股文"); System.out.println(bloomFilter.contains("Hollis")); System.out.println(bloomFilter.contains("王巨星")); //关闭客户端 redisson.shutdown();
或者Jedis
Jedis jedis = new Jedis("localhost"); jedis.bfCreate("myfilter", 100, 0.01); jedis.bfAdd("myfilter", "Hollis"); jedis.bfAdd("myfilter", "666"); jedis.bfAdd("myfilter", "八股文"); System.out.println(jedis.bfExists("myfilter", "Hollis")); System.out.println(jedis.bfExists("myfilter", "王星星")); jedis.close();