一 bloomfilter的作用
1.1 作用
Bloomfilter:默认是有0组成bit数组和hash函数构成的数据结构,用来判断在海量数据中是否存在某个元素。
应用案例:解决缓存穿透。Bloomfilter放在redis前面,如果查询bf中没有则直接返回,如果存在则查询redis,如果redis不存在,则查询mysql数据库。bf拦截一些不必要的请求。
1.2 案例
1.2.1 流程逻辑
1.构建过程
1)预加载符合条件的记录;2)计算每条记录的hash值;3)计算hash值对应bitmap数组的位置;4)修改值为1;
2.查询过程
1)计算元素的hash位置;2)计算hash值对应二进制数组的位置;3)找到数组中对应位置的值,0代表不存在;1代表存在。
1.2.2 setbit的构建过程
1.@postConstruct初始化白名单数据
2.计算元素的hash值
3.通过上一步的hash值算出对应的二进制数组的坑位,将对应坑位的值修改为数字;1表示存在。
1.2.3 查询是否存在
1.计算元素的hash值;2通过上一步的hash值计算出对应的二进制数组对应的坑位,返回对应坑位的值,0表示无;1表示存在。
二 工程搭建
2.1 工程结构
2.2 不使用bloomfilter过滤器的情况
2.2.1 新增初始化数据
1.controller
2.service
2.2.2 查询数据
1.controller
2.service
2.2.3 初始化数据库
1.数据库配置
2.数据库脚本
CREATE TABLE `t_customer` (
`id` bigint(11) NOT NULL,
`cname` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`sex` varchar(255) DEFAULT NULL,
`birth` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.2.4 启动服务测试
1.页面访问新增数据
http://localhost:8083/customer/add
2.访问查询接口
访问存在的数据id=1
访问不存在的数据id=3
控制台显示信息:
2023-07-31 18:44:11.850 INFO 4172 --- [nio-8083-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
刚开始redis不存在。。。。。。
3.查看数据库接口
4.查看redis
2.3 使用bloomfilter过滤器的情况
2.3.1 逻辑架构
2.3.2 初始化白名单数据
这里将customer:12作为key,存储到redis数据库中。
@Component
@Slf4j
public class BloomFilterInit
{
@Resource
private RedisTemplate redisTemplate;
@PostConstruct //初始化白名单数据
public void initWhiteListData(){
//1.白名单客户加载到布隆过滤器中
String key="customer:12";
//2.计算hashvalues,由于存储计算出来可能存在负数,我们取绝对值
int hashValue=Math.abs(key.hashCode());
//3.通过hashvalue和2的32次方后取余,获得对应的下标坑位
long index=(long)(hashValue%Math.pow(2,32));
log.info("key"+"对应的index:"+index);
//4.设置redis里面的bitmap对应的白名单 whitelistcustomer的坑位,将改值设置为1;
redisTemplate.opsForValue().setBit("whiteListCustomer",index,true);
}
}
截图
2.3.3 使用bloomfilter过滤判断
public Customer findDataByBloomFilter(Integer id){
//1.封装key
String key=CACHA_KEY_CUSTOMER+id;
//2.布隆过滤器check,无是绝对无,有是可能有
//===============================================
if(!checkUtils.checkWithBloomFilter("whiteListCustomer",key))
{
log.info("白名单无此顾客,不可以访问: "+key);
return null;
}
//3.查询redis
Customer customer=(Customer) redisTemplate.opsForValue().get(key);
if(customer==null){
System.out.println("redis不存在。。。。。。");
//4.redis为空,查询mysql
customer=customerDao.selectByPrimaryKey(id);
if(customer!=null){
System.out.println("redis不存在,查询mysql存在");
//5.mysql中数据存在, 把mysq查询出来的数据回写redis,保持一致性
redisTemplate.opsForValue().set(key,customer);
System.out.println("redis不存在,查询mysql存在,回写redis最新数据....");
}
}
return customer;
}
checkutil工具类:
package com.ljf.redis.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @auther zzyy
* @create 2022-12-27 14:56
*/
@Component
@Slf4j
public class CheckUtils
{
@Resource
private RedisTemplate redisTemplate;
public boolean checkWithBloomFilter(String checkItem,String key)
{
int hashValue = Math.abs(key.hashCode());
long index = (long)(hashValue % Math.pow(2,32));
boolean existOK = redisTemplate.opsForValue().getBit(checkItem,index);
log.info("--->key:"+key+" 对应坑位下标index: "+index+" 是否存在:"+existOK);
return existOK;
}
}
2.截图
2.3.4 判断测试
1.访问customer:1 但是初始白名单放入的是customer:12; 不存在则一定过滤掉。
控制台:
2023-07-31 18:47:43.243 INFO 4172 --- [io-8083-exec-10] com.ljf.redis.util.CheckUtils : --->key:customer:1 对应坑位下标index: 1581185131 是否存在:false
2023-07-31 18:47:43.243 INFO 4172 --- [io-8083-exec-10] c.l.r.service.impl.CustomerServiceImpl : 白名单无此顾客,不可以访问: customer:1
2.修改初始化白名单,customer:12 改为customer:1
3.再次启动服务,再次访问
4.控制台信息
2023-07-31 19:10:08.794 INFO 19236 --- [nio-8083-exec-2] com.ljf.redis.util.CheckUtils : --->key:customer:1 对应坑位下标index: 1581185131 是否存在:true
5.redis中查看
127.0.0.1:6379> get whiteListCustomer
@
127.0.0.1:6379>