Redis:缓存穿透、缓存雪崩和缓存击穿
Redis的缓存穿透、缓存雪崩和缓存击穿
- 一. 缓存穿透
- 1.1 概念
- 1.3 解决方案
- 1.4 案例:查询商铺信息(缓存穿透的实现)
- 二. 缓存雪崩
- 2.1 概念
- 2.2 解决方案
- 三. 缓存击穿(热点key)
- 3.1 概念
- 3.2 解决方案
- 3.3 案例:查询商铺信息(互斥锁和逻辑过期的实现)
- (1)互斥锁
- 补充:SETNX(setIfAbsent)
- (2)逻辑过期
一. 缓存穿透
1.1 概念
缓存模型;
定义:指客户请求的数据在缓存中和数据库中都不存在!这样缓存永远不会命中,请求都会发到数据库;
(穿透即穿透了缓存和数据库);
问题:假设不断发送这样的请求,则会让数据库崩溃;
1.3 解决方案
(1)主动(拦截非法请求)
- 增加请求参数的复杂度且有一定规范,对请求参数提前做校验;
- 加强用户权限校验,如用拦截器校验登录状态;
- 热点参数的限流
(2)被动(请求已经进来了)
- 缓存空值:
为了不再让请求造成缓存穿透,则产生一个null空值(value=””
)到redis中,当下次再请求时就可以从Redis返回而不用去查数据库;
优点:简单
缺点:
①造成Redis中的内存消耗;----- 对这样的null值数据设置TTL有效时间;
②短期不一致:Redis中存了null值,但是数据库此时真的增加了这条数据,而缓存中数据和数据库不一样,当有用户来查询时会查到null数据;------ 缩短TTL时间 或者手动覆盖数据到Redis中
;
- 布隆过滤
【在请求访问Redis之前】添加一个布隆过滤器;
布隆过滤器中存储的是byte字节数据,存储的是二进制位;
数据库中的数据会经过Hash算法,将计算后的值存到布隆过滤器,当请求发过来时,会判断请求的Hash值是否存在于过滤器中,若过滤器判断不存在则一定不存在;
若过滤器判断存在,则只是可能存在,不是100%准确,所以依然有一定缓存穿透风险的;
优点:内存占用少,只需要存Hash值,没有多余key
缺点:实现相对复杂;可能误判(哈希冲突);
1.4 案例:查询商铺信息(缓存穿透的实现)
业务逻辑:
1.注入stringRedisTemplate的bean,从Redis查询商户缓存,如果命中则判断是不是空值
,不是则返回商铺信息,是则返回 “店铺不存在“;
2.未命中则查询数据库,如果是空值则报错;
3.若数据库中存在,则将商铺信息更新到Redis中,并更新TTL
有效时间(兜底)!
4.如果数据库中也不存在(缓存穿透),则key=id,value=”“
写入Redis;
效果:
发送id不存在的查询请求,查询Redis和数据库,缓存穿透了,返回“不存在”,会在Redis缓存一个空值;
假设再发送同样的请求,则查询到Redis中的空字符串数据,还是返回“不存在”,但是不走数据库了!
总结:1. 缓存穿透则写入空值 2. Redis命中时需要判断是否为空字符串;
二. 缓存雪崩
2.1 概念
定义:同一时段内有①大量的缓存Key同时失效过期 或者 ②Redis服务宕机,导致大量请求发往数据库,带来巨大压力;
2.2 解决方案
(1)针对大量缓存同时失效:
- 给不同的key的添加随机的TTL;
- 给缓存业务添加降级限流策略,牺牲部分服务,保护数据库;
(2)针对Redis宕机:
- 启用熔断机制暂停服务;
- 用主从节点的方式建立Redis高可用集群,哨兵机制 可以实现对服务的监控(类比Kafka),主从实现数据同步;
- 在多个层面建立缓存,让Nginx也做缓存,Nginx未命中,再去Redis,再去JVM,最后才是数据库
多级缓存在大型电商中如商品详情会使用,以应对亿级以上的并发;
三. 缓存击穿(热点key)
3.1 概念
定义:
热点数据
:业务通常会有几个数据会被 ①频繁访问,比如秒杀活动,且 ②缓存重建业务比较复杂。
如果缓存中的某个热点数据过期了且缓存重建耗时较长,此时大量的请求线程都会去查询数据库、并缓存重建,会对数据库带来巨大冲击;
3.2 解决方案
(1)互斥锁
查询的时候,一旦Redis未命中,所有请求都去尝试获取锁,获取锁成功的线程去缓存重建;
而获取锁失败的线程则 sleep休眠一会再查询,直到锁释放后,再访问到新的缓存;
这样就只有一个线程去访问数据库了;
优点:保持数据的一致性
缺点:线程需要等待,性能不好(牺牲可用性)
(2)逻辑过期
- 缓存穿透的原因就是因为热点数据过期,所以设置一个
逻辑过期
让热点数据在Redis中永不过期(不设置TTL); - 封装原始数据,再新增一个
过期时间
的字段放到Value中,让线程可以判断是否逻辑过期;(类似MyBatisPlus的逻辑删除) - 理论上都能命中,判断:如果逻辑删除字段没有过期则直接返回Redis中数据;
- 线程发现热点数据已经逻辑过期,就尝试获取
互斥锁
,
①如果获取锁成功,则再 开启一个新的线程 去 缓存重建,原线程返回旧数据;
②获取失败则返回旧数据;
优点:线程不需要等待,性能好(保证可用性)
缺点:牺牲一致性
注意:
互斥锁方法和逻辑过期都用了互斥锁,前者当其他线程发现锁被占用后则会一直重试等待直到缓存重建完成;
后者当其他线程发现数据逻辑过期后才去尝试获取锁,锁被占用则获取旧的热点数据,而不会一直等待;
总结:互斥锁和逻辑过期都是在解决缓存重建时的并发问题; 互斥锁即让请求线程等待,保证一致性
;逻辑过期是可以拿到旧数据即保证了可用性
,但是牺牲了一致性
。
3.3 案例:查询商铺信息(互斥锁和逻辑过期的实现)
(1)互斥锁
controller层传入参数为url中的商铺id,请求方式为 GET;
流程:
- 先定义加锁(
setIfAbsent/SETNX
)和解锁(delete
)两个成员方法: - 基于查询缓存模型,从Redis查询商户缓存,如果命中则序列化、返回商铺信息,
- 如果未命中则尝试获取锁,输入key=商铺id,
①获取锁失败则休眠一会(Thread.sleep()
),再重新尝试查询Redis(递归);
②获取锁成功则缓存重建,缓存重建需要去查询数据库,查完后序列化、更新Redis、设置TTL(一致性兜底) - finally中释放锁,避免死锁,返回结果;
先定义加锁和解锁两个成员方法:
传入锁的key=店铺id,value从1开始(不重要),则加锁或者释放锁;
为了防止锁永不释放,给锁加有效期(兜底),返回Boolean
表示加锁是否成功;
解锁就是直接删除key;
业务逻辑:
就算抛异常最后也要 释放锁,所以将释放锁放在finally
中;
测试:
使用 JMeter
模拟多线程并发的场景;
设置1000个线程,时间共5s,QPS
≈200;
结果:
Idea中显示只查询了一次数据库,成功;
补充:SETNX(setIfAbsent)
SETNX / setIfAbsent
:(NX=not exist)即key不存在时才能set成功(获取锁)!key已经存在则不能获取锁(锁已被占用), 用来作 互斥锁
使用;
例:
即相当于是互斥锁,只有第一个能成功!后面的都失败了!
释放锁:
为了防止锁永远不被释放,一般加有效期(兜底):假设10秒,一般业务执行在1秒内;
(2)逻辑过期
缓存穿透的原因就是因为热点数据过期
,所以设置一个逻辑过期让热点数据永不过期
;
即并不是真正的过期,而是将过期时间字段放到Value中,由开发人员判断是否过期,而不是TTL;
①创建含有逻辑过期时间的实体类(原始数据+逻辑过期时间
):
创建一个RedisData
实体类建立expireTime
逻辑过期时间属性(LocalDateTime类型)即逻辑过期时间,用RedisData对象封装原始shop对象!
②封装原始shop数据,并存入RedisData数据到Redis:
ShopServiceImpl中:
1.根据id查询shop
商铺类型的对象
2.创建新的实体类RedisData
(包含shop对象+逻辑过期时间),将shop对象 封装 到RedisData对象中;
3.设置RedisData对象的逻辑过期属性expireTime
;
4.将RedisData对象即热点数据 存入 Redis,key是shopID+”前缀”;
plusSeconds()
为在当前时间加多少秒,时间由参数传入;加了之后就是逻辑的过期时间了!
Redis中存的RedisData的JSON数据:
③查询(用逻辑过期解决缓存击穿):
ShopServiceImpl中:
由于Redis中热点数据是提前添加好的,理论上一定命中!
则缓存穿透也不用考虑;
1.由于没有用TTL,则理论上Redis一定会命中 !
而为了程序的健壮性才判断是否命中,如果未命中直接返回null即可;
2.命中后,判断是否逻辑过期,需要反序列化为RedisData类型;
3.如果未过期,直接返回数据;
用工厂方法创建 线程池
:
由线程池去创建线程并执行;
4.如果过期了,则需要缓存重建;尝试获取锁,
①如果获取锁成功,则再开启一个线程去缓存重建,这里使用 线程池
,将缓存重建任务提交到线程池,然后原线程返回旧数据;
②获取失败则返回旧数据;
这里线程池的任务执行不需要用返回值,使用execute()方法提交Runable类型任务也可以;
Redis:缓存穿透、缓存雪崩和缓存击穿相关推荐
- Redis 缓存穿透、雪崩、缓存数据库不一致、持久化方式、分布式锁、过期策略
1. Redis 缓存穿透 1.1 Redis 缓存穿透概念 访问了不存在的 key,缓存未命中,请求会穿透到 DB,量大时可能会对 DB 造成压力导致服务异常. 由于不恰当的业务功能实现,或者外部恶 ...
- 二十七、Redis缓存穿透和雪崩(完)
Redis缓存穿透和雪崩 一.服务的高可用问题 在这里我们不会详细的区分析解决方案的底层! Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面.但同时,它也带来了一些问题.其中 ...
- Redis -- 缓存穿透和雪崩
文章目录 一.缓存穿透 1.1 概念 1.2 解决方案 1.3 布隆过滤器的工作原理 二.缓存击穿 2.1 概念 2.2 解决方案 三.缓存雪崩 3.1 概念 3.2 解决方案 用户的数据一般是存储于 ...
- Redis全部知识总结(概念、安装、用法、数据类型、事务、持久化、Jeids、订阅系统、缓存穿透及雪崩等)
Redis NoSql简述 Nosql概念 Nosql的四大分类 Redis概述 Redis的安装 安装文件 Window下安装 Linux下安装 redis-benchmark 压力测试工具 五大数 ...
- 21_Redis_浅析Redis缓存穿透和雪崩
为什么了解缓存穿透和雪崩:保证服务的高可用问题 Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面.但同时,它也带来了一些问题.其中,最要害的问题,就是数据的一致性问题,从严格 ...
- Redis11_缓存穿透和雪崩
Redis11_缓存穿透和雪崩 缓存穿透 用户查询某个数据时,会先在redis缓存中查询,如果缓存没有命中,会去持久层数据库MySQL中查询,如果此时依然没有命中,将返回null,不会写入缓存. 当同 ...
- Redis中的缓存穿透、雪崩、击穿的原因以及解决方案(详解)
一.概述 ① 缓存穿透:大量请求根本不存在的key(下文详解) ② 缓存雪崩:redis中大量key集体过期(下文详解) ③ 缓存击穿:redis中一个热点key过期(大量用户访问该热点key,但是热 ...
- 一篇吃透Redis缓存穿透、雪崩、击穿问题
前言:在学Redis之前我们查询数据的时候都是直接查询数据库的,但是这样会有一个潜在的问题:"如果用户量很大,所有请求都去访问数据库,那么会使数据库压力过大,导致性能下降甚至宕机" ...
- beyond compare4过期解决方法_面试必备:缓存穿透、雪崩解决方案及缓存击穿的四种解决方案...
前言 设计一个缓存系统,不得不要考虑的问题就是:缓存穿透.缓存击穿与失效时的雪崩效应. 缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到 ...
- mysql数据库击穿_面试中经常出现的缓存穿透、雪崩和击穿到底是什么?
对于缓存穿透.缓存雪崩和缓存击穿常常出现在面试中,今天来看看它到底是何方神圣 redis缓存穿透 理解重在穿透吧,也就是访问透过redis直接经过mysql,通常是一个不存在的key,在数据 ...
最新文章
- python语言程序设计——深入学习python
- LightOJ 1084 Winter(记忆化搜索)
- js-FCC算法Smallest Common Multiple。找出两个参数和它们之间的连续数字的最小公倍数。...
- Soul网关发布2.2.0 | 让高性能网关变得如此简单!
- scala部分应用函数_Scala中的部分函数
- LeetCode 1334. 阈值距离内邻居最少的城市(最短路径Dijkstra)
- python循环迭代_Python中循环迭代的重做
- 神马专车喊话特斯拉:修不修、赔不赔、认不认?
- 如何利用ansible callback插件对执行结果进行解析
- memcached-tool
- CATIA—机电产品数字化设计工具
- Win10外接显示器可以复制不可以扩展
- Illegal mix of collations
- 总线揭密:串行传输VS并行传输
- 【WEB】Web性能压力测试工具
- Bit、Byte、KB
- ie调用java闪退_win7旗舰版系统位数不兼容导致IE浏览器频繁出现闪退怎么办
- i春秋-Web(一)
- 挠性电路板和刚性电路板的区别,以及柔性电路板焊接方法操作步骤
- jsp 学习笔记( java编译报错:程序包 javax.servlet和javax.servlet.http 不存在)