Redis缓存场景

Redis缓存场景

Redis做缓存

部分图解来自于:https://www.mianshiya.com/

Redis由于高性能,通常可以作为数据库存储的缓存,比如给MySQL当缓存。

旁路缓存

Cache Aside,即旁路缓存,最常见的模式。应用服务把缓存作为数据库的旁路,直接和缓存进行交互。

读操作:

1.应用服务收到查询请求,先去查询数据是否在缓存上

2.如果在,把缓存数据打包返回;如果不在,去访问数据库查询,然后放到缓存上

image-20240821163309099

写操作:

旁路缓存一般都是先更新数据库,然后直接删除缓存。

image-20240821163407392

为什么不是更新缓存,而是删除缓存呢?因为更新比删除更容易导致时序性问题。

比如:

thread1更新一个MySQL字段为1,thread2更新MySQL字段为2,thread2更新缓存为2,thread1更新缓存为1。

最终缓存被更新为1,正确的数据因为时序性被覆盖了。

缓存异常

缓存穿透

缓存穿透指,缓存和数据库中都没有的数据,而用户不断发起请求。

如果从存储层查不到则不写入缓存,这个导致不存在的数据每次都请求到存储层去,失去了缓存的意义。

在流量大时,DB可能就挂了,如果有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

image-20240821165426228

解决方案:

1.接口层增加校验,如用户鉴权,id做基础校验,id<=0直接拦截

2.缓存中取不到的数据,数据库也取不到,将key-value设置为key-null,设置到缓存中(设置一个过期时间,如30秒,时间太长,正常情况也没法使用)。这样可以防止一个用户反复用同一个id暴力攻击

3.使用布隆过滤器,布隆过滤器可以快速判断某个元素是否存在于集合中。

布隆过滤器是一种巧妙的数据结构,可以用来查询,某个元素可能存在或一定不存在。

布隆过滤器的原理:

底层为一个bit数组,将字符串用多个哈希函数映射到不同位置,将对应位置置为1。

在查询时,如果一个字符串所有哈希函数映射的值都为1,那么数据可能存在;否则不存在。

为什么说可能呢?因为其他字符也可能占据这个bit位的值。

缓存击穿

缓存击穿是指缓存中没有数据库有的数据(一般都是缓存时间到期),由于并发量特别大,同时读缓存没有读到缓存,又同时去数据库去数据,引起数据库压力瞬间增大,造成过大压力。

缓存击穿,一般指热键在过期失效的一瞬间,还没来得及重新生产,就有海量数据,直达数据库。

解决方案:

1.热点数据支持续期,持续访问的数据可以不断续期,避免因为续期而失效而被击穿

2.发现缓存失效,重建缓存加互斥锁,当线程查询缓存发现缓存不存在就会尝试加锁,线程争抢锁,拿到锁的线程进行查询数据库,然后重建缓存,争抢锁失败的线程,加一个睡眠然后循环重试。

image-20240821181555510

缓存雪崩

缓存雪崩,指大量的应用请求因为异常无法在Redis缓存中进行处理,像雪崩一样,直接打到数据库。

异常原因主要是:缓存中数据大批量到过期时间,而查询数据量大,引起数据库压力增大甚至宕机。

image-20240821181619863

和缓存击穿不同的是,缓存击穿是指一条热点数据没在Redis及时重建,缓存雪崩是大批量数据在Redis同时失效。

解决方案:

1.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发送。

2.重建缓存加互斥锁,当线程拿到缓存后发现缓存不存在就会尝试加锁,线程争抢锁,拿到锁的线程进行查询数据库,重建缓存,争抢锁失败的线程加一个睡眠然后循环重试。

缓存一致性

部分图解来自于:https://www.mianshiya.com/
不推荐使用:

1.先写缓存再写数据库

2.先写数据库再写缓存

3.先删除缓存再写数据库

推荐使用:

4.先写数据库再删缓存

5.缓存双删

6.Binlog异步更新

先写缓存再写数据库

部分图解来自于:https://www.mianshiya.com/
image-20240821183018394

由于网络问题,请求顺序无法保证,有可能出现,后更新缓存的请求反而先更新了数据库。

先写数据库再写缓存

部分图解来自于:https://www.mianshiya.com/
image-20240821183203219

同样的,后更新数据库的请求先更新了缓存。

先删除缓存再写数据库

部分图解来自于:https://www.mianshiya.com/
image-20240821183353280

读操作读到了过期的数据,虽然写操作已经完成了,但是因为缓存被删除了,读操作必须从数据库读到了更新前的旧值。

先写数据库再删缓存

部分图解来自于:https://www.mianshiya.com/
image-20240821183650246

先修改数据库的值,再去删除对应缓存,在修改数据库期间允许一定时间的缓存不一致,保证最终程序一致性。

但是也会有一定的问题:

image-20240821183842529

在写操作时,并发有个读请求,又因为缓存失效,读请求在写操作之后更新缓存,导致数据库和缓存不一致。

缓存双删

缓存双删,先删除数据库,再写数据库,过一段时间再删除缓存
部分图解来自于:https://www.mianshiya.com/
image-20240821192917342

避免数据被回种到缓存,间隔一段时间删除缓存。

也可以使用消息队列、定时任务或延迟任务实现删除:

image-20240821193139898

Binlog异步更新

部分图解来自于:https://www.mianshiya.com/
image-20240821193309413

先修改数据库,然后通过Canal监听数据库的binlog日志,记录数据库的修改信息,然后通过消息队列异步修改缓存的数据。这里注意保证顺序消费,保证缓存中数据按顺序更新,然后加上重试机制,避免网络问题导致更新失败。

总结

1.一般不使用前三种方式

2.后三种根据使用场景进行选择

  • 实时一致性:考虑先写MySQL,再删Redis(有短期不一致,但它能尽量保证数据一致性)
  • 最终一致性:考虑binlog+消息队列(因为重试和顺序消费,最大限度保证缓存与数据库最终一致性)
  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值