Redis 缓存更新策略以及什么是缓存穿透,击穿,雪崩

132 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天

缓存更新策略的最佳实践方案

  1. 低一致性需求时: 使用redis自带的内存淘汰机制就可以了
  2. 高一致性需求时: 主动更新,在更新数据库的时候就更新我们的缓存,这个是更新还是删除缓存呢,这个选择删除缓存,因为更新缓存有很多的无效写操作。

对象高并发,线程安全问题我们又如何呢????

读操作:我们一般都是先命中缓存,如果没有再去查询数据库,再写入缓存,设定有效时间

写操作: 我们决定先操作数据库,再删除缓存 ,要确保数据库与缓存的操作的原子性,对于分布式我们可以使用分布式事务,要么同时失败要么同时成功,对于单体系统,把对缓存的操作和数据库的操作放在一个事务就好了

2. 下面我们来看看什么是缓存穿透

缓存穿透:故名思义,穿过了缓存,那为什么会发生穿透呢,穿过缓存不是正常的吗,其实是请求在缓存中没有命中到,就去了我们的数据库结果数据库也没查询的,这样如果有一些恐怖分子逮到这个机会,对我们的这个请求发起了攻击,这样很大可能造成数据库驾崩,那么就造成了缓存穿透,首先我们来看看两种解决方案

  1. 缓存空对象 优点:实现简单,维护方便

    缺点:可能会造成额外的内存消耗,造成短暂的数据不一致

2.使用布隆过滤器

优点:内存占用少,没有多余的key
缺点:实现比较复杂,存在误判的可能性

大家这时候肯定会这么想:: 当用户发来请求时,布隆进行拦截,如果数据库中没有数据节则拦截,那么它是怎么知道数据库里有没有数据的呢,其实布隆的大致思想是这样的,布隆采用的是一种特殊的算法,会将数据库的数据进行统计转成二进制的形式保存在布隆过滤器中,之后来判断一个请求到底能不能请求的到数据,如果布隆里没有,数据库肯定没有. 布隆有误差性。

3. 增强id的复杂性 避免id的规律被发现,尽量使用长度一致的id

  1. 对热点参数进行限流

  2. 对请求进行权限管理 等等

那么如何对参数进行限流呢??????????????????

1.2 漏桶算法:

漏桶算法的基本思想是: 利用一个缓存区,当有请求进入系统时,无论请求的速率如何,都先在缓存区内保

存,然后以固定的流速流出缓存区进行处理。漏桶算法的特点是无论外部请求压力如何,漏桶算法总是以固定

的流速处理数据。漏桶的容积和流出速率是该算法的两个重要参数。

1.2 令牌桶算法:

令牌桶算法是一种反向的漏桶算法。在令牌桶算法中,桶中存放的不再是请求,而是令牌。

处理程序只有拿到令牌后,才能对请求进行处理。

如果没有令牌,那么处理程序要么丢弃请求,要么等待可用的令牌。

为了限制流速,该算法在每个单位时间产生一定量的令牌存入桶中。

比如,要限定应用每秒只能处理 1 个请求,那么令牌桶就会每秒产生 1 个令牌。

通常,桶的容量是有限的,比如,当令牌没有被消耗掉时,只能累计有限单位时间内的令牌数量。

3. 我们来看看什么是缓存雪崩

缓存雪崩:当大量请求打到缓存上时,这时缓存的大多数key同时过期了,导致请求全部打到了数据库,这样就会给数据库带来巨大压力,很有可能造成服务宕机的可能.

2.2 还有一种可能就是 redis服务直接宕机了,这时又有大量请求打到数据库,又有可能造成数据库服务宕机,这是非常危险的。

解决方案: 1.使用redis集群,reid的哨兵机制,当主节点挂了,会自动选举一台服务作为主节点,这里还有主从同步的机制在里面,redis的主从同步是异步进行的。 主节点有的数据,子节点也有.

  1. 添加多级缓存

  2. 给缓存业务添加限流服务

  3. 给TTL增加随机值,很简单就随机数。

原理很简单的说就是:

(1)从向主发起SYNC指令,主调用BGSAVE进行持久化工作,同时将写指令缓存在内存中;
(2)BGSAVE完成后,主将持久化好的RDB文件发给从,从收到后将其存入磁盘,再读到内存中,该动作完成后,主再将缓存的写指令发给从。

  1. 那么什么是缓存击穿呢????

击穿::大家想想,击穿肯定就是对某个点进行击穿,也就是对于某个热点key有大量的并发请求发起,这时这个key如果突然失效了,那么大量的请求又会打到数据库中,给数据库造成巨大压力,影响了性能,甚至服务宕机,.

解决方案有两种

1.互斥锁

那么什么是互斥锁,怎么用呢,首先,当第一个线程进来的时候,

  1. 查询缓存未命中, 2.则去获取锁,
  2. 然后查询数据库重建缓存,
  3. 释放锁

当第二个第三个线程进来的时候,缓存未击中,则会获取锁,如果获取不到,会暂时休眠一会,然后再重新查询缓存,大家想想,这样肯定会影响性能,后面的线程都在等待了,十分影响性能,好处是保证了数据一致性。

好处:实现简单,就用一个互斥锁,保证一致性 坏处:影响性能,可能会有死锁的可能(死锁就是假设说在我们这个业务里我们对多个缓存有查询需求,而对于另一个业务也是,如果在获取缓存锁后你发现另一个缓存锁在另一个业务里,于是就是产生这种互相等待的问题。)

2.逻辑过期

2.1 逻辑过期什么意思额,就是在给key设置缓存的时候,加个exprie过期时间值,跟保存的时候的过期时间是不一样的哦.

2.2 当第一个线程进来的时候,如果缓存未命中, 则去获取互斥锁,获取成功,会开启一个线程去给我查询数据库创建缓存,写入缓存,重置逻辑过期时间。(这是时间是当前时间,再加上个30分钟之类的), 当后面线程进来的时候,如果没命中,也没拿到锁,则会查询旧的数据,返回过期的数据。只有在创建缓存的线程执行结束后进来的线程才能拿到最新的数据

坏处:内存消耗,不能保证一致性,实现复杂

好处:性能高,线程不用等待.

总结:不同业务使用不同的解决方案,谨慎使用

累了一天了,总结文章来提升自己。还是那句话,你的鼓励是我最大的动力。拜拜啦