① 缓存雪崩
缓存雪崩是指在短时间内,有⼤量缓存同时过期,导致⼤量的请求直接查询数据库,从⽽对数据库造成 了巨⼤的压⼒,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。
我们先来看下正常情况下和缓存雪崩时程序的执⾏流程图,正常情况下系统的执⾏流程如下图所示:
缓存雪崩的执⾏流程,如下图所示:
以上对⽐图可以看出缓存雪崩对系统造成的影响,那如何解决缓存雪崩的问题?
缓存雪崩的常⽤解决⽅案有以下⼏个。
加锁排队
加锁排队可以起到缓冲的作⽤,防⽌⼤量的请求同时操作数据库,但它的缺点是增加了系统的响应时 间,降低了系统的吞吐量,牺牲了⼀部分⽤户体验。
随机化过期时间
为了避免缓存同时过期,可在设置缓存时添加随机时间,这样就可以极⼤的避免⼤量的缓存同时失效。
// 缓存原本的失效时间
int exTime = 10 * 60;
// 随机数⽣成类
Random random = new Random();
// 缓存设置
jedis.setex(cacheKey, exTime+random.nextInt(1000) , value);
设置⼆级缓存
⼆级缓存指的是除了 Redis 本身的缓存,再设置⼀层缓存,当 Redis 失效之后,先去查询⼆级缓存。 例如可以设置⼀个本地缓存,在 Redis 缓存失效的时候先去查询本地缓存⽽⾮查询数据库。 加⼊⼆级缓存之后程序执⾏流程,如下图所示:
② 缓存穿透
缓存穿透是指查询数据库和缓存都⽆数据,因为数据库查询⽆数据,出于容错考虑,不会将结果保存到 缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。
缓存穿透执⾏流程如下图所示:
其中红⾊路径表示缓存穿透的执⾏路径,可以看出缓存穿透会给数据库造成很⼤的压⼒。 缓存穿透的解决⽅案有以下⼏个。
缓存空结果
另⼀种⽅式是我们可以把每次从数据库查询的数据都保存到缓存中,为了提⾼前台⽤户的使⽤体验 (解 决⻓时间内查询不到任何信息的情况),我们可以将空结果的缓存时间设置的短⼀些,例如 3-5 分钟。
③ 缓存击穿
缓存击穿指的是某个热点缓存,在某⼀时刻恰好失效了,然后此时刚好有⼤量的并发请求,此时这些请 求将会给数据库造成巨⼤的压⼒,这种情况就叫做缓存击穿。
缓存击穿的执⾏流程如下图所示:
它的解决⽅案有以下 2 个。
加锁排队
此处理⽅式和缓存雪崩加锁排队的⽅法类似,都是在查询数据库时加锁排队,缓冲操作请求以此来减少 服务器的运⾏压⼒。
设置永不过期
对于某些热点缓存,我们可以设置永不过期,这样就能保证缓存的稳定性,但需要注意在数据更改之 后,要及时更新此热点缓存,不然就会造成查询结果的误差。
④ 缓存预热
⾸先来说,缓存预热并不是⼀个问题,⽽是使⽤缓存时的⼀个优化⽅案,它可以提⾼前台⽤户的使⽤体验。
缓存预热指的是在系统启动的时候,先把查询结果预存到缓存中,以便⽤户后⾯查询时可以直接从缓存 中读取,以节约⽤户的等待时间。
缓存预热的执⾏流程,如下图所示:
缓存预热的实现思路有以下三种:
- 1. 把需要缓存的⽅法写在系统初始化的⽅法中,这样系统在启动的时候就会⾃动的加载数据并缓存数 据;
- 2. 把需要缓存的⽅法挂载到某个⻚⾯或后端接⼝上,⼿动触发缓存预热;
- 3. 设置定时任务,定时⾃动进⾏缓存预热。