前言
缓存穿透概念
当查询一个一定不存在的数据,由于缓存不命中,去查询数据库也无法查询出结果,因此不会写入到缓存中,这会导致每个查询都去请求数据库,造成缓存穿透。
解决方案:
布隆过滤对所有的可能查询的参数以hash形式存储,在控制器层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力。bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。举例:将真实正确Id在添加完成之后便加入到过滤器当中,每次再进行查询时,先确认要查询的Id是否在过滤器中,如果不在,则说明Id为非法Id。
缓存空对象当从数据库查询不到值,就把参数和控制缓存起来,设置一个简短的过期时间(因为缓存是需要内存的,如果有过多空值key,占用内存多),在该时间段如果有携带此参数再次请求,就可以直接返回。可能导致该段时间缓存层和数据库数据不一致,对于需要保持一致性的业务有影响。
小编觉得学习的话,就得视频+代码+课件配合着学习,这样才能够理解的最够透彻,掌握到知识的精髓,这不,已经都给大家准备好了,大家可以好好学习一波!!!!
BloomFilter_Test.java (手写布隆过滤器代码)
import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
import java.nio.charset.Charset;
public class BloomFilter_Test {
private JedisPool jedisPool = null;
private Jedis jedis = null;
//要存储的数据量··
private static long n = 10000;
//所能容忍错误率
private static double fpp = 0.01F;
//bit数组长度
private static long numBits = optimalNumOfBits(n, fpp);
//hash函数个数
private int numHashFunctions = optimalNumOfHashFunctions(n, numBits);
public static void main(String[] args) {
System.out.println(numBits);
// long[] indexs = new BloomFilter_Test().getIndexs("hello");
BloomFilter_Test filterTest = new BloomFilter_Test();
filterTest.init();
int ex_count = 0;
int ne_count = 0;
/**
* 存在:不一定存在
* 不存在:一定不存在
*/
for (int i = 0; i < 20000; i++) {
// filterTest.put("bf",100 + i + "");
boolean exist = filterTest.isExist("bf", 100 + i + "");
if(exist){
ex_count++;
}else{
ne_count++;
}
}
//ex_count:6729 ne_count 3271
System.out.println("ex_count:" + ex_count + "\t" + "ne_count " + ne_count);
}
public void init(){
//测试连接redis
jedisPool = new JedisPool("192.168.150.111", 6379);
jedis = jedisPool.getResource();
}
private long getCount(){
Pipeline pipeline = jedis.pipelined();
Response<Long> bf = pipeline.bitcount("bf");
pipeline.sync();
Long count = bf.get();
pipeline.close();
return count;
}
/**
* 判断keys是否存在于集合where中
*/
public boolean isExist(String where, String key) {
long[] indexs = getIndexs(key);
boolean result;
//这里使用了Redis管道来降低过滤器运行当中访问Redis次数 降低Redis并发量
Pipeline pipeline = jedis.pipelined();
try {
for (long index : indexs) {
pipeline.getbit(where, index);
}
result = !pipeline.syncAndReturnAll().contains(false);
} finally {
pipeline.close();
}
// if (!result) {
// put(where, key);
// }
return result;
}
/**
* 将key存入redis bitmap
*/
private void put(String where, String key) {
long[] indexs = getIndexs(key);
//这里使用了Redis管道来降低过滤器运行当中访问Redis次数 降低Redis并发量
Pipeline pipeline = jedis.pipelined();
try {
for (long index : indexs) {
pipeline.setbit(where, index, true);
}
pipeline.sync();
/**
* 把数据存储到mysql中
*/
} finally {
pipeline.close();
}
}
/**
* 根据key获取bitmap下标方法来自guava
*/
public long[] getIndexs(String key) {
long hash1 = hash(key);
long hash2 = hash1 >>> 16;
long[] result = new long[numHashFunctions];
for (int i = 0; i < numHashFunctions; i++) {
long combinedHash = hash1 + i * hash2;
if (combinedHash < 0) {
combinedHash = ~combinedHash;
}
result[i] = combinedHash % numBits;
}
return result;
}
/**
* 获取一个hash值 方法来自guava
*/
private long hash(String key) {
Charset charset = Charset.forName("UTF-8");
return Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asLong();
}
private static int optimalNumOfHashFunctions(long n, long m) {
return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
}
private static long optimalNumOfBits(long n, double p) {
if (p == 0) {
p = Double.MIN_VALUE;
}
return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
}
}
布隆过滤器算法课件
Redis缓存穿透终极解决方案,手写布隆过滤器,学习视频+代码+课件
关注公众号:Java架构师联盟,文章首发公众号
好的东西就要分享给大家学习!