帮你解读什么是Redis缓存穿透、缓存击穿和缓存雪崩(包含解决方案)
前言
作为一种非关系型数据库,redis也总是免不了有各种各样的问题,这篇文章主要是针对其中三个问题进行讲解:缓存穿透、缓存击穿和缓存雪崩,并给出一些解决方案。
一、缓存穿透
1、概念
缓存穿透
是指查询一个数据库一定不存在的数据
。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。
这里需要注意缓存击穿
的区别,缓存击穿,缓存击穿是指缓存中没有但数据库中有的数据
,并且某一个key非常热点
,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间(一般是缓存时间到期),持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
为了避免缓存穿透其实有很多种解决方案。下面介绍几种
2、解决方案
(1)布隆过滤器
布隆过滤器
是一个bit向量或者bit,如果我们要映射一个值到布隆过滤器中,我们使用多个不同的哈希函数生成多个哈希值,并将每个生成的哈希值指向的bit位设置为1,如下baidu一词设置了三个位置为1。
原理:对一个key进行k个hash算法获取k个值,在比特数组中将这k个值散列后设定为1,然后查的时候如果特定的这几个位置都为1,那么布隆过滤器判断该key存在。
“tencent”一词,对应的情况
可以看到,不同的词对应的bit位置可能相同,当词很多的情况时,可能大部分bit位置都是1,这时查询taobao可能对应的位置都为1,只能说明taobao一词可能存在,不是一定存在的,这时1就被覆盖了,这就是布隆过滤器的误判。如果它说不存在那肯定不存在,如果它说存在,那数据有可能实际不存在。
Redis的bitmap只支持2^32大小,对应到内存也就是512MB,误判率万分之一,可以放下2亿左右的数据,性能高,空间占用率及小,省去了大量无效的数据库连接。
因此我们可以通过布隆过滤器,将Redis缓存穿透控制在一个可容范围内。
使用布隆过滤器:
导入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
代码:
public class Test {
private static int size = 1000000;//预计要插入多少数据
private static double fpp = 0.01;//期望的误判率
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, fpp);
public static void main(String[] args) {
//插入数据
for (int i = 0; i < 1000000; i++) {
bloomFilter.put(i);
}
int count = 0;
for (int i = 1000000; i < 2000000; i++) {
if (bloomFilter.mightContain(i)) {
count++;
System.out.println(i + "误判了");
}
}
System.out.println("总共的误判数:" + count);
}
}
应用:
@Cacheable(value="key1")
public String get(String key) {
String value = redis.get(key);
// redis中不存在该缓存
if (value == null) {
//布隆过滤器也没有,直接返回
if(!bloomfilter.mightContain(key)){
return null;
}else{
//布隆过滤器中能查到,不代表一定有,查出来放入redis,同样也可以避免缓存穿透
value = db.get(key);
redis.set(key, value);
}
}
return value;
}
(2)、缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。
但是这种方法会存在两个问题:
● 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键
,因为这当中可能会有很多的空值的键;
● 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
二、缓存雪崩
(1)、概念缓存雪崩
是指缓存中大批量数据到过期时间
,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据
,缓存雪崩是不同数据
都过期了,很多数据都查不到从而查数据库。
产生雪崩的原因之一,假如马上就要到双十一零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。
博主在做电商项目的时候,一般有三种方法:
(1)采取不同分类商品,缓存不同周期。在同一分类中的商品,加上一个随机因子。这样能尽可能分散缓存过期时间
,而且,热门类目的商品缓存时间长一些,冷门类目的商品缓存时间短一些
,也能节省缓存服务的资源。
(2)如果缓存数据库是分布式部署,将 热点数据均匀分布在不同的缓存数据库中。
(3)设置热点数据永远不过期。
(4) 使用加锁限流的方式
三、缓存击穿
(1)概念缓存击穿
,是指缓存中没有但数据库中有的数据
,并且某一个key非常热点
,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间(一般是缓存时间到期),持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
(2)解决方案
● 设置热点数据永远不过期。
● 使用互斥锁(mutex key)
业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
} else {
return value;
}
}
4、原创 | 万万没想到,HashMap默认容量的选择,竟然背后有这么多思考!?
扫描二维码
获取更多精彩
Java技术大联盟
有收获,就点个在看
本文分享自微信公众号 - Java技术大联盟(jingdakunye520)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
微软最新声明:若断供中国Windows,概不负责!
最近,微软更新了其官网的Microsoft服务协议,声明如果因为不可抗力导致微软无法履行或延迟履行其义务,微软对此不承担任何责任或义务,新的协议将于10月1日正式生效。 近日,微软更新了Microsoft服务协议,其中一些条款耐人寻味。 根据该协议,如果美国政府禁止微软给任何中国企事业单位或者个人使用微软的软件,微软概不负责。 若Windows系统不能继续使用,还有备胎可选吗? 微软更新服务协议,若「断供」不负责 最新版Microsoft协议将于10月1日正式生效。 责任限制条款的b条内容指出: 对于因超出微软合理控制范围的情况(例如,劳资纠纷、不可抗力、战争或恐怖主义行为、恶意破坏、意外事故或遵守任何适用法律或政府命令)而导致微软无法履行或延迟履行其义务,微软对此不承担任何责任或义务。 如果美国政府也对微软下达禁令,微软可能无法继续支持Windows服务,并且不承担因此引发的任何后果,只会尽最大努力避免影响。 在最新版的协议中,签约实体、法律选择和争议解决地一项指出: 「当您依照这些条款使用所有其他服务时,您将与微软公司 (One Microsoft Way, Redmond, WA...
- 下一篇
Oracle大势已去,国产数据库的春天来了
提起大名鼎鼎的oracle,估计大家都知道,最近几年,oracle在全球占了数据库市场的大约45%,第二名和第三名分别是IBM的DB2和微软的sqlserver。 而国内的数据库市场,Oracle占了56%,第二第三也依然被DB2和sqlserver占据,mysql也是一个比较流行的数据库,但商用很少,大多用于研究学习用途的。 先算一笔账,笔者做过很多软件项目,Oracle的费用占据不少成本,Oracle有两种收费方式: 按照用户个数:按50个User License无限使用期的购买量则价格为17.5万。 按照CPU个数:每个CPU License无限使用期的价格为17万9千(多核需要系数进行换算) 按IBM小机的系数计算,则购买价格为17万9千,和50个User License的价格相近。 事实上,一个企业项目都是最少好几百用户,如果严格按照Oracle公司的收费方式的话, 一个项目算下来全都白干,利润都是甲骨文公司了。所以,大部分公司购买数据库的时候都会走点偏门。 国内软件项目长期给Oracle打工的情形,估计很快就变了,国产数据库替代一触即发,因为最近,中美贸易战频频发生,美国芯...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS6,CentOS7官方镜像安装Oracle11G
- Linux系统CentOS6、CentOS7手动修改IP地址
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8安装Docker,最新的服务器搭配容器使用