一、缓存穿透常用解决方案有那些
减轻访问数据库压力。
缓存穿透的问题?
缓存的穿透:指定使用一些不存在的key
进行大量的查询Redis,导致无法命中
每次的请求都会要传查询到我们数据库;对我们的数据库的压力非常大;
解决方法:
1.对我们的服务接口api实现限流
、用户授权
、黑名单和白名单拦截
;(最核心)
2.从缓存和数据库都查询不到结果的话,一样将数据库空值结果缓存到Redis
中;设置30s的有效期
避免使用同一个id对
数据库攻击。
如果黑客真的在攻击的情况下,随机成id肯定是不一样的。
3.布隆过滤器
二、布隆过滤器简单的介绍
1、什么是布隆过滤器呢?
HashMap
? 检测该key是否存在 原理:先计算对应的key存放到数组的Index? O(1)
(在hash值没有产生冲突情况下)
2、布隆过滤器 作用:
布隆过滤器适用于判断一个元素在集合中是否存在
,但是可能
会存在误判
的问题。概率极低
实现的原理采用二进制向量数组
和随机映射hash函数
。
三、布隆过滤器实现原理的分析
3.布隆过滤器为什么会产生冲突
会根据key计算hash值,可能与布隆过滤器中存放的元素hash产生冲突都是为1,布隆可能会产生误判可能存在。
比如:
mayiket: 可以拆分成3个字节 ,分别对应的hash值是:
ma | yi | ket |
---|---|---|
4 | 10 | 13 |
yushengjun可以拆分成3个字节 ,分别对应的hash值是:
yush | eng | jun |
---|---|---|
13 | 16 | 19 |
这个时候来了 一个吗 meit。也可以拆分成3个字节 ,分别对应的hash值是:
me = ma 的哈希值 4
i= eng 的哈希值是 16
t = ket的哈希值是13
3个字节的hash值都找到了,但是 他只是刚刚好和已经存在 的 字节相同。本身并不存在。但是会误判为 存在。
如何解决这个问题:
二进制数组长度设置比较大
,可以减少布隆误判的概率。
四、java语言使用布隆过滤器使用
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import java.util.ArrayList;
public class BlongTest {
/**
* 假设集合中存放100万条数据
*/
private static Integer size = 1000000;
public static void main(String[] args) {
BloomFilter<Integer> integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.0000000001);
for (int i = 0; i < size; i++) {
// 向我们布隆过滤器中存放100万条数据
integerBloomFilter.put(i);
}
ArrayList<Integer> integers = new ArrayList<>();
for (int j = size; j < size + 10000; j++) {
// 使用该pai判断key在布隆过滤器中是否存在 返回true 存在 false 表示不存在
if (integerBloomFilter.mightContain(j)) {
// 将布隆过滤器误判的结果存放到集合中方便后期统计
integers.add(j);
}
}
System.out.println("布隆过滤器误判的结果:" + integers.size());
// 0.03概率 数组长度730万左右 0.01
// 0.01概率 数组长度960万左右
}
}
误判概率不能是0
五、布隆过滤器如何减少误判的概率
源码误判概率是:3%
六、使用布隆过滤器解决Redis穿透问题
@RequestMapping("/dbToBulong")
public String dbToBulong() {
// 1.从数据库预热id到布隆过滤器中
List<Integer> orderIds = orderMapper.getOrderIds();
integerBloomFilter = BloomFilter.create(Funnels.integerFunnel(), orderIds.size(), 0.01);
for (int i = 0; i < orderIds.size(); i++) {
// 添加到我们的布隆过滤器中
integerBloomFilter.put(orderIds.get(i));
}
return "success";
}
@RequestMapping("/getOrder")
public OrderEntity getOrder(Integer orderId) {
// 0.判断我们的布隆过滤器
if (!integerBloomFilter.mightContain(orderId)) {
System.out.println("从布隆过滤器中查询不存在");
return null;
}
// 1.先查询Redis中数据是否存在
OrderEntity orderRedisEntity = (OrderEntity) redisTemplateUtils.getObject(orderId + "");
if (orderRedisEntity != null) {
System.out.println("直接从Redis中返回数据");
return orderRedisEntity;
}
// 2. 查询数据库的内容
System.out.println("从DB查询数据");
OrderEntity orderDBEntity = orderMapper.getOrderById(orderId);
if (orderDBEntity != null) {
System.out.println("将Db数据放入到Redis中");
redisTemplateUtils.setObject(orderId + "", orderDBEntity);
}
return orderDBEntity;
}
项目要求不严谨的时候,可以用 布隆过滤器。
1.从数据库预热id到布隆过滤器中 , 添加到我们的布隆过滤器中
2.判断我们的布隆过滤器
3.先查询Redis中数据是否存在
4. 查询数据库的内容
@RequestMapping("/addOrder")
public String addOrder() {
// 1.提前生成订单token 临时且唯一
String orderToken = UUID.randomUUID().toString();
Long orderId = System.currentTimeMillis();
// 2.将我们的token存放到rdis中
redisUtils.setString(orderToken, orderId + "", 10L);
OrderEntity orderEntity = new OrderEntity(null, "永久", orderId + "", orderToken);
return orderMapper.insertOrder(orderEntity) > 0 ? "success" : "fail";
}
redis工具类
@Component
public class RedisTemplateUtils {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public void setObject(String key, Object value) {
setObject(key, value, null);
}
public void setObject(String key, Object value, Long timeOut) {
redisTemplate.opsForValue().set(key, value);
if (timeOut != null) {
redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
}
}
public Object getObject(String key) {
return redisTemplate.opsForValue().get(key);
}
}
@Component
public class RedisUtils {
/**
* 获取我们的redis模版
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void setString(String key, String value) {
setString(key, value, null);
}
public void setString(String key, String value, Long timeOut) {
stringRedisTemplate.opsForValue().set(key, value);
if (timeOut != null) {
stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
}
}
public String getString(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
/**
* redis当成数据库中
*
* 注意事项:对我们的redis的key设置一个有效期
*/
}