redis缓存预热、缓存雪崩、缓存击穿、缓存穿透、缓存降级、缓存更新
缓存预热
缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
缓存预热就是在 redis 启动的时候,就开始往 redis 中写了数据,然后再给应用提供服务。而不是在应用访问的时候才开始往 redis 中写数据。
那么启动的时候,应该往 redis 中写入什么样的数据呢?
- 应该根据当天的具体的访问情况,进行实时的统计出访问量频率较高的数据,即热数据,比如热门商品,热门博客等,把这部分数据提前写入 redis集群中。我们不需要把所有的数据都写入 redis中,也没有这个必要,一是数据量会很大,写入的数据会很慢,二是访问量不大的数据也没有必要写入 redis 中。
- 如果热数据比较多的话,我们可以启动多个服务并行读取数据去写入 redis 集群,进行分布式的缓存预热。
- 当把热数据写入 redis 集群后,才开始对处提供服务。那么这样,大部分的请求都会走 redis 集群,而不需要访问 mysql 数据库。
怎么解决?
- 首先,通过 nginx + lua 的方式,把访问流量数据上报到 Kafka,也可以是其它的 mq 队列。
- 然后使用实时计算框架(如 storm 、spark streaming、flume)从 kafka中消费访问流量数据,实时计算出访问频率高的数据,这里统计出来的可能只会有编号信息,如商品编号或博客编号等。
- 最后,根据编号从 mysql 数据库中查询出具体的信息,写入 redis,开始提供服务。
还有一种解决思路:
- 直接写个缓存刷新页面,上线时手工操作下。
- 数据量不大,可以在WEB系统启动的时候加载。
- 定时刷新缓存,
缓存雪崩
缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的出现 (约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。
数据库服务器崩溃
- 1、系统平稳运行过程中,忽然数据库连接量激增
- 2、应用服务器无法及时处理请求
- 3、大量408,500错误页面出现
- 4、客户反复刷新页面获取数据
- 5、数据库崩溃
- 6、应用服务器崩溃
- 7、重启应用服务器无效
- 8、Redis服务器崩溃
- 9、Redis集群崩溃
- 10、重启数据库后再次被瞬间流量放倒
缓存雪崩
- 1、在一个
较短的时间内,缓存中较多的key集中过期
- 2、此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据
- 3、数据库同时接收到大量的请求无法及时处理
- 4、Redis大量请求被积压,开始出现超时现象
- 5、数据库流量激增,数据库崩溃
- 6、重启后仍然面对缓存中无数据可用
- 7、Redis服务器资源被严重占用,Redis服务器崩溃
- 8、Redis集群呈现崩塌,集群瓦解
- 9、应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃
- 10、应用服务器,redis,数据库全部重启,效果不理想
问题分析
- 短时间范围内
- 大量key集中过期
解决思路
- 1、更多的页面静态化处理
- 2、构建多级缓存架构
Nginx缓存+redis缓存+ehcache缓存 - 3、检测Mysql严重耗时业务进行优化
对数据库的瓶颈排查:例如超时查询、耗时较高事务等 - 4、灾难预警机制
监控redis服务器性能指标- CPU占用、CPU使用率
- 内存容量
- 查询平均响应时间
- 线程数
- 5、限流、降级
短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问
解决方案
- 1、LRU与LFU切换
- 2、数据有效期策略调整
- 根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟
- 过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量
- 3、超热数据使用永久key
- 4、定期维护(自动+人工)
对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时 - 5、加锁 慎用!
参考链接:https://blog.csdn.net/qq_29373285/article/details/88544299
缓存击穿
缓存击穿
就是单个高热数据过期的瞬间
,数据访问量较大,未命中redis后,发起了大量对同一数据的数据库访问,导致对数据库服 务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度 较高,配合雪崩处理策略即可。
缓存击穿和缓存雪崩很相似,缓存雪崩是针对所有的key,缓存击穿针对的是某一个key
场景,假如秒杀一个商品的key,但是这个key过期了,对这个商品的并发量过高。缓存中没有但数据库中有的数据(一般是缓存时间到期)
,这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
解决方案
- 1、
设置热点数据永远不过期
。 - 2、
接口限流与熔断,降级
。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务
不可用时候,进行熔断,失败快速返回机制。 - 3、
布隆过滤器
。bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是 否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小, - 4、
加互斥锁,
互斥锁参考代码如下:
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private Jedis jedis;
private final String MUTEX_KEY = "MUTEX_";
public String getData(String key) throws InterruptedException {
String value = stringRedisTemplate.opsForValue().get(key);
//缓存失效
if (StringUtils.isBlank(value)) {
//设置分布式锁,只允许一个线程去查询DB,同时指定过期时间为1min,防止del操作失败,导致死锁,缓存过期无法加载DB数据
if (tryLock(MUTEX_KEY + key, 60L)) {
//从数据库查询数据,将查询的结果缓存起来
value = getValueFromDB();
stringRedisTemplate.opsForValue().set(key, value);
//释放分布式锁
stringRedisTemplate.delete(MUTEX_KEY + key);
} else {
//当锁被占用时,睡眠5s继续调用获取数据请求
Thread.sleep(5000);
getData(key);}
}
return value;
}
/**
* redis实现分布式事务锁 尝试获取锁
*
* @param lockName 锁
* @param expireTime 过期时间
* @return
*/
public Boolean tryLock(String lockName, long expireTime) {
//RedisCallback redis事务管理,将redis操作命令放到事务中处理,保证执行的原子性
String result = stringRedisTemplate.opsForValue().getOperations().execute(new RedisCallback<String>() {
/**
* @param key 使用key来当锁,因为key是唯一的。
* @param value 请求标识,可通过UUID.randomUUID().toString()生成,解锁时通value参数可识别出是哪个请求添加的锁
* @param nx 表示SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作
* @param ex 表示过期时间的单位是秒
* @param time 表示过期时间
*/
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
return jedis.set(lockName, UUID.randomUUID().toString(), "NX", "EX", expireTime);
}
});
if ("OK".equals(result)) {
return true;
}
return false;
}
public String getValueFromDB() {
return "";
}
缓存穿透
缓存穿透是指缓存和数据库中都没有的数据,
而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
上一篇: DFS深度优先搜索最详细讲解
下一篇: Guava cache 本地缓存