Redis相关概念:缓存击穿、雪崩、穿透、预热、降级、一致性等
Redis缓存雪崩、缓存击穿、缓存预热热点key、缓存降级、短链接、分布式锁秒杀、预减库存、
堆外缓存+Redis架构设计、Redis动态刷新、Redis和DB双写一致性、过期删除策略、集群数据倾斜等
一、缓存雪崩
缓存雪崩是指当缓存中的大量数据在同一时间失效,导致大量请求直接访问数据库,从而引起数据库的压力过大,
严重影响系统的性能。
redis 主机挂了, Redis全盘崩溃,偏硬件运维
redis 中有大量key 同时过期大面积失效,偏软件开发
1、缓存数据的随机过期时间
在缓存数据的过期时间上增加随机因素,从而避免大量数据在同一时间失效的情况
//将数据缓存到Redis中,并增加随机的过期时间
//int random = new Random().nextInt(600) + 600;
2、预热缓存
在系统启动时预热缓存,将系统中的热点数据提前加载到缓存中,从而避免了大量请求同时访问数据库的情况。
@Component
public class AdmdvsDimCacheManager implements InitializingBean {
@Override
public void afterPropertiesSet() {
//预热处理
}
}
3、分布式锁
使用分布式锁,从而避免大量请求同时访问数据库的情况。
public List<User> getUserList() {
List<User> userList = redisTemplate.opsForValue().get("userList");
if (userList == null) {
// 如果Redis中不存在,则尝试获取分布式锁
RLock lock = redissonClient.getLock("userListLock");
try {
// 尝试加锁,并设置锁的过期时间为5秒
boolean success = lock.tryLock(5, TimeUnit.SECONDS);
if (success) {
// 如果获取到了锁,则查询数据库并将数据缓存到Redis中
userList = userDao.getUserList();
if (userList != null && userList.size() > 0) {
redisTemplate.opsForValue().set("userList", userList, 1, TimeUnit.HOURS);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock
}
} return userList;
}
二、缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无 数的请求访问会在瞬间给数据库带来巨大的冲击。
描述:假设线程1在查询缓存之后,本来应该去查询数据库,然后把这个数据重新加载到缓存的, 此时只要线程1走完这个逻辑,其他线程就都能从缓存中加载这些数据了,但
是假设在线程1没有走完的 时候,后续的线程2,线程3,线程4同时过来访问当前这个方法, 那么这些线程都不能从缓存中查询到 数据,那么他们就会同一时刻来访问查询缓存,
都没查到,接着同一时间去访问数据库,同时的去执行 数据库代码,对数据库访问压力过大.
1、互斥锁
如果未命中缓存,先获取互斥锁,获取锁之后要再次检查缓存,如果还是未命中进行缓存重建,这样当其他线程来的时候就会获取锁失败,这时我们让这个线程休眠一会,重新
查询缓存,如果命中就返回嘛,如果没命中再次尝试获取锁,假设这次获取锁成功了,还是再次检查缓存,如果未命中重建缓存。
描述:假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人 去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有
获得到锁,那么线程2就可以进行到休 眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了。
优点:可保证数据高一致性
缺点:性能低,可能发生死锁
2、逻辑过期
我们把过期时间设置在 redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续 通过逻辑去处理。假设线程1去查询缓存,然后从value中判断出来当
前的数据已经过期了,此时线程1 去获得互斥锁,那么其他线程会进行阻塞,获得了锁的线程他会开启一个 线程去进行 以前的重构数据 的逻辑,直到新开的线程完成这个逻辑
后,才释放锁, 而线程1直接进行返回,假设现在线程3过来访 问,由于线程线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把 重建数据构
建完后,其他线程才能走返回正确的数据。
优点:性能高
缺点:数据可能不一致,实现复杂
三、缓存穿透
缓存穿透是指查询一个不存在的数据,由于缓存中没有数据,请求会直接穿透到数据库中,从而引起数据库的压力过大,严重影响系统的性能。
1、布隆过滤器
布隆过滤器是一种高效的数据结构,可以判断一个元素是否存在于一个集合中,同时也可以减轻数据库的压力。在使用布隆过滤器的时候,首先将所有的数据hash到一个位图
中,如果查询的数据在位图中不存在,那么直接返回不存在,从而避免了对数据库的查询操作。
优点:内存占用较少,没有多余key
缺点:实现复杂,存在误判可能
2、空对象存储
采用空对象缓存的方式,即当查询的数据不存在时,将一个空对象缓存到Redis中。这样下次查询同样不存在的数据时,就可以直接从Redis中获取到一个空对象,从而避免了
对数据库的查询操作。
优点:实现简单,方便维护
缺点:占内存,可能造成短期数据不一致
四、缓存预热热点KEY
缓存预热是指在系统启动或者高峰期之前,提前将数据加载到缓存中,避免在用户请求的时候,先查询数据库(这样第一个查询的人就会比较慢),再把查询结果回写到redis
当中去。
通过LRU数据删除策略,可以得到高频数据队列。
通过脚本的方式将热点数据提前加入。
处理方案:
1、配置一个job定时刷新数据缓存。
2、直接写个缓存刷新页面,上线后手动刷新。
3、项目启动的时候直接将热点KEY缓存到redis
五、缓存降级
当站点的访问量剧增、服务出现卡顿问题或非关键服务影响到关键流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,
放弃一些不重要的加载项,也可以配置开关实现人工降级。
降级的最终目的是保证关键核心服务的正常运行,比如涉及财务的网站购物的购物车,结算等重要模块,是不能降级的。
六、缓存一致性
缓存一致性指的是缓存与DB之间的数据一致性,我们需要通过各种手段来防止缓存与DB不一致,我们要保证缓存与DB的数据一致或者数据最终一致。
处理方案:
1.数据库层:
在数据库层面,可以使用事务来确保数据的一致性。通过将读写操作放在同一个事务中,可以保证数据的更新和查询是一致的。
使用数据库的触发器或者存储过程,在数据更新的同时,主动触发缓存的更新操作,确保缓存与数据库的数据保持一致。
2.缓存层:
在缓存层面,可以使用缓存更新策略,通过定时任务、异步消息队列等方式,定期或者在数据更新时异步地更新缓存,保持缓存与数据库的数据一致性。
使用互斥锁或者分布式锁来保证对缓存的读写操作的原子性,避免数据冲突。
设置合适的缓存过期时间,避免缓存数据长时间过期而导致数据不一致的问题。
3.应用层:
在应用层面,可以采用读写分离策略,将读请求和写请求分发到不同的节点,读请求直接从缓存中获取数据,写请求则更新数据库并更新缓存,保持数据的一致性。
使用缓存中间件或者缓存组件,提供自动更新缓存的功能,减少手动维护缓存的复杂性。
4.监控和报警:
建立监控和报警机制,通过监控缓存层和数据库层的状态、数据一致性等指标,及时发现异常情况,并触发报警,以便及时处理问题。
标签:缓存,06,数据库,Redis,查询,线程,数据
From: https://blog.csdn.net/pilot_speed/article/details/145299996