redis的缓存设计

缓存设计#

缓存雪崩

为了保证缓存中的数据和数据库中的数据一致性,一般都会给redis的数据设置过期时间TTL,当缓存的数据过期后若用户访问的那份数据需要重写生成缓存,会查询数据库,然后更新到缓存中。

image-20230814204957772

如果当大量缓存数据在同一时间过期或失效,或redis宕机,此时有大量请求无法走缓存,会打到数据库中,导致数据库压力骤增,甚至造成数据库宕机等,造成系统崩溃。

原因:

  • 大量数据同时过期
  • Redis故障宕机

解决策略:

对于大量数据同时过期:

  • 均匀设置过期时间:避免将大量数据设置成同一个过期时间,可以在对缓存数据设置过期时间时,给这些数据的过期时间加上一个随机数。

  • 互斥锁:当业务线程处理用户请求时若访问的数据不在redis,就加一个互斥锁,保证同一时间只有一个请求来构筑缓存,当缓存构筑完毕后再释放锁,没有获取到锁的请求要么等锁释放重新读缓存,要么返回默认值。同时,实现互斥锁时最好设置一个TTL,避免意外导致阻塞不释放锁。

  • 后台线程更新缓存:业务线程不再负责更新缓存,缓存不设置有效期,将更新缓存的工作交给后台线程定时执行。但存在一个问题,即系统内存紧张时会将一些缓存数据淘汰,在淘汰后直到下一次后台线程更新缓存都是不在redis中的,此时业务方看来就是数据丢失。

    解决:

    • 后台线程不仅负责定时更新缓存,同时也负责频繁检测缓存是否有效,若因为系统紧张被淘汰就要从数据库读然后更新到缓存。
    • 在后台线程发现缓存数据失效后通过消息队列发送一条消息通知后台线程更新缓存,后台线程收到消息后在更新缓存前判断该缓存是否存在,存在就不更新;不存在就去数据库读数据然后缓存。

    业务上线时应该提前把数据缓存起来,这就是缓存预热,可以用后台更新缓存机制来实现。

对于Redis故障宕机:

  • 服务熔断或请求限流机制:Redis宕机后可以启动服务熔断机制,暂停业务对缓存服务的访问,直接返回错误。服务熔断保证了数据库的正常运行,但暂停了业务系统访问缓存服务,导致全部业务无法正常工作。为此可以采用限流机制,只将少部分请求发送到数据库处理,其余的在入口拒绝。
  • 构建Redis缓存高可靠集群:通过主从节点的方式构建Redis缓存高可靠集群,若主节点宕机可以切换到从节点。
缓存击穿

热点数据:被频繁访问的数据称为热点数据

如果缓存中的某个热点数据过期了,大量的访问请求没有在缓存找到该数据,导致大量请求打到数据库,从而让数据库宕机服务宕机。

和缓存雪崩类似,可以采取缓存雪崩的方案:

  • 互斥锁方案
  • 不给热点数据设置过期时间,由后台线程异步更新。或在热点数据将要过期前通知后台线程更新缓存以及重新设置过期时间。
缓存穿透

当用户访问的数据既不在缓存中,也不在数据库中,导致请求访问缓存找不到,访问数据库也找不到,无法构建缓存。当有大量这种请求访问时会导致数据库压力骤增甚至崩溃。

发生原因:

  • 业务误操作,缓存中的数据和数据库中的数据都被误删,导致缓存和数据库中都没有数据。
  • 黑客恶意攻击,故意大量访问不存在的数据

解决方案:

  • 非法请求限制:在API入口判断请求参数是否合理,若判断是恶意请求就直接返回错误,避免进一步访问缓存和数据库。
  • 缓存空值或默认值:当查询不到缓存和数据库时可以在缓存中将该对应数据设置为空值,这样后续请求可以直接读到空值或默认值,不会走数据库了。
  • 用布隆过滤器判断数据是否存在,避免通过查询数据库来判断:可以在服务启动时将存在的数据在布隆过滤器做标记,在每次写入数据库时也做一个标记,然后在用户请求到来时当业务线程确认缓存失效后可以通过布隆过滤器来判断数据是否存在,不需要再去查询数据库。
总结

image-20230814213640738

缓存一致性
先更库再更缓存

问题:

image-20230815200551191

会出现缓存和数据库中的数据不一致的现象。

但如果业务对缓存命中率要求很高的话可以采用该方案,但需要做一些并发控制:

  • 更新缓存前先加分布式锁,保证同一时间只有一个请求更新缓存,但对写入性能有影响
  • 在更新缓存后,给缓存加上一个较短的过期时间,即使出现缓存不一致的情况也能很快过期。
先更缓存再更库

问题:

image-20230815200638793

总结

先更库或先更缓存,在高并发的情况下都会导致缓存和数据库不一致。因此要采用删除缓存,读取数据时缓存没有数据再去数据库中读然后更新到缓存中。即旁路缓存策略。

先删缓存后更库

image-20230815201904874

在并发情况下,先删缓存再更新数据库还是会出现数据库缓存不一致。

解决方案是延迟双删

先更库后删缓存

image-20230815202226021

理论上也会出现数据不一致的问题,但实际上出现的概率较小,因为缓存写入的速度非常快,远快于写入数据库。但如果没有删除成功,就会导致缓存和数据库不一致,如果缓存没有过期时间就导致数据一直没有更新。

因此,先更数据库再删缓存可以保证数据一致性。但删除了缓存,会降低系统的缓存命中率。

解决删除失败的方案

  • 重试机制:引入消息队列,将删除缓存要操作的数据加入队列,如果删除失败可以从消息队列重新读取数据,然后再次删除缓存,重试一定次数仍然失败则发送报错消息。

  • 订阅binlog再操作缓存:当更新数据成功时会产生一条变更日志,可以通过订阅binlog日志拿到具体要操作的数据,然后再执行缓存删除。可以利用canal中间件。

    image-20230815205251189

作者:墨鱼-yyyl

出处:https://www.cnblogs.com/moyu-yyyl/p/18009696

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   墨鱼yyyl  阅读(55)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示