**前言:**在我们使用缓存来减少数据库在高并发下的压力时,常见的三种问题:缓存穿透,缓存雪崩,缓存击穿。本文简单介绍了三种问题的基本概念以及大致的解决方案。具体方案,请结合实际情况选择。欢迎讨论!有错漏处,还请大佬们多多指点。
redis做缓存。
1.缓存穿透
- 正常访问时,可以直接从redis拿到数据,减轻数据库的压力。当大量并发去访问一个数据库不存在的数据,由于缓存中没有该数据导致大量并发查询数据库,这个现象就是缓存穿透。
- 缓存穿透可以造成数据库瞬间压力过大,连接数等资源用完,最终数据库拒绝连接不可用。
如何解决缓存穿透?
- 对请求增加校验机制 比如:课程Id是长整型,如果发来的不是长整型则直接返回。
- 使用布隆过滤器。布隆过滤器可以用于检索一个元素是否在一个集合中。如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。
- 3、缓存空值或特殊值。需要注意设置过期时间,不宜过长。
2.缓存雪崩
- 缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。
- 造成缓存雪崩问题的原因是是大量key拥有了相同的过期时间,比如对课程信息设置缓存过期时间为10分钟,在大量请求同时查询大量的课程信息时,此时就会有大量的课程存在相同的过期时间,一旦失效将同时失效,造成雪崩问题。
弄清楚雪崩的原因,就可以针对性的解决。
-
使用同步锁控制查询数据库的线程。使用同步锁控制查询数据库的线程,只允许有一个线程去查询数据库,查询得到数据后存入缓存。但是不要全部锁住,应缩小加锁范围,比如只锁数据库操作部分。在锁里面也可以进行校验锁,避免刚写入缓存,然后被打进数据。双重校验锁
synchronized(obj){ if(缓存中已经存在){ //访问缓存 } //查询数据库 //存入缓存 }
-
对同一类型信息的key设置不同的过期时间通常对一类信息的key设置的过期时间是相同的,这里可以在原有固定时间的基础上加上一个随机时间使它们的过期时间都不相同。
//设置过期时间300秒 redisTemplate.opsForValue().set("course:" + courseId, JSON.toJSONString(coursePublish),300+new Random().nextInt(100), TimeUnit.SECONDS);
-
缓存预热
不用等到请求到来再去查询数据库存入缓存,可以提前将数据存入缓存。使用缓存预热机制通常有专门的后台程序去将数据库的数据同步到缓存。就是写一个定时同步任务。
3.缓存击穿
- 缓存击穿是指大量并发访问同一个热点数据,当热点数据失效后同时去请求数据库,瞬间耗尽数据库资源,导致数据库无法使用。
如何解决?
- 使用同步锁控制
- 设置key永不过期