文章目录
- 布隆过滤器BloomFilter
- 布隆过滤器是什么、能干嘛
- 原理
- hash冲突导致数据不精确
- 缓存预热
- 缓存雪崩
- 发生
- 预防
- 缓存穿透
- 缓存穿透是什么
- 解决
- 工作中哪里使用过redis
布隆过滤器BloomFilter
布隆过滤器是什么、能干嘛
由一个初值都为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素。
能够高效的插入和查询,占用空间少,但返回的结果是不确定、不够完美。一个元素如果判断存在,元素不一定存在;但是判断结果为不存在,则一定不存在。布隆过滤器可以添加元素,但是不能删除元素,由于涉及hashcode判断依据,删掉元素会导致误判率增加
原理
布隆过滤器是一种专门用来解决去重问题的高级数据结构。实质就是一个大型位数组和几个不同的无偏hash函数(无偏表示分布均匀)。由一个初值都为零的bit数组和多个个哈希函数构成,用来快速判断某个数据是否存在。但是跟 HyperLogLog 一样,它也一样有那么一点点不精确,也存在一定的误判概率
(1)添加key时:使用多个hash函数对key进行hash运算得到一个整数索引值,对位数组长度进行取模运算得到一个位置,每个hash函数都会得到一个不同的位置,将这几个位置都置1就完成了add操作。
(2)查询key时:只要有其中一位是零就表示这个key不存在,但如果都是1,则不一定存在对应的key。
hash冲突导致数据不精确
(1)哈希函数的概念是:将任意大小的输入数据转换成特定大小的输出数据的函数,转换后的数据称为哈希值或哈希编码,也叫散列值。如果两个散列值是不相同的(根据同一函数)那么这两个散列值的原始输入也是不相同的。这个特性是散列函数具有确定性的结果,具有这种性质的散列函数称为单向散列函数。散列函数的输入和输出不是唯一对应关系的,如果两个散列值相同,两个输入值很可能是相同的,但也可能不同,这种情况称为“散列碰撞(collision)”。用 hash表存储大数据量时,空间效率还是很低,当只有一个 hash 函数时,还很容易发生哈希碰撞。
(2)布隆过滤器的误判是指多个输入经过哈希之后在相同的bit位 置1了,这样就无法判断究竟是哪个输入产生的,这种情况也造成了布隆过滤器的删除问题,因为布隆过滤器的每一个 bit 并不是独占的,很有可能多个元素共享了某一位。如果我们直接删除这一位的话,会影响其他的元素
缓存预热
缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
缓存雪崩
发生
(1)redis主机挂了
(2)redis中有大量key同时过期造成大面积失效
预防
(1)redis缓存集群实现高可用
(2)多缓存结合:使用本地缓存+redis缓存
(3)服务降级,如sentine限流或降级
缓存穿透
缓存穿透是什么
(1)请求去查询一条记录,先查redis无,后查mysql无,都查不到该条记录,每次查询都要访问数据库,导致后台数据库压力暴增,这种现象我们称为缓存穿透。
(2)缓存穿透带来的问题是,当有大量请求查询数据库不存在的数据时,就会给数据库带来压力,甚至会拖垮数据库。
解决
(1)空对象缓存或者缺省值(零、负数、defaultNull)
(2)可以使用布隆过滤器解决缓存穿透的问题把已存在数据的key存在布隆过滤器中,相当于redis前面挡着一个布隆过滤器。当有新的请求时,先到布隆过滤器中查询是否存在:
①如果布隆过滤器中不存在该条数据则直接返回;
②如果布隆过滤器中已存在,才去查询缓存redis,如果redis里没查询到则再查询Mysql数据库
工作中哪里使用过redis
(1)创建任务编号
private int createCodeExec(){
String key = "COUNTER:LINE_TASK_CODE_SERIAL";
Long serial = redisTemplate.opsForValue().increment(key); //如果这个key在缓存中不存在则初始化值为0,并返回1作为增加后的值
Long counterExpire = redisTemplate.getExpire(key);
if(counterExpire<0){
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR_OF_DAY,1);
calendar.set(Calendar.MINUTE,0);
calendar.set(Calendar.SECOND,0);
calendar.set(Calendar.MILLISECOND,0);
redisTemplate.expireAt(key, calendar.getTime()); //设置key失效时间为当前时间的下一个小时整点的时间
}
// 获取当前时间,并按照 "MMddHH000" 的格式转换为字符串,然后将其解析为一个整数。
int num = Integer.parseInt(DateTime.now().toString("MMddHH000"));
// String.format("%1$-" + 9 + "s", num) 的作用是将 num 格式化为一个长度为9的字符串,并使用空格在右侧进行填充,以实现右对齐
// replace(' ', '0') 将空格替换为0
num = Integer.valueOf(String.format("%1$-" + 9 + "s", num).replace(' ', '0'));
return (int) (num+serial); // 将num和redis生成的value值进行相加
}