常见缓存策略与缓存一致

1. 常见的缓存策略?

  • Cache Aside(旁路缓存)策略;
  • Read/Write Through(读穿/写穿)策略
  • Write Back(写回)策略

实际开发中,Redis和MySQL的更新策略是用的Cache Aside

1.1 Cache Aside(旁路缓存)策略

旁路缓存策略是最常用的,应用程序直接与数据库、缓存交互,并负责对缓存的维护,该策略又可以细分为读策略写策略

写策略的步骤

  • 更新数据库中的数据,再删除缓存中的数据

读策略的步骤

  • 如果读取的数据命中了缓存,则直接返回数据;
  • 如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。

注意:写策略的顺序不能倒过来,不能先删缓存再更新数据库,并发的时候,容易数据不一致。

先更新数据库再删除缓存」不会有数据不一致的问题?

理论分析上会出现数据不一致的情况,但是在实际中,这个问题出现的概率并不高,因为写数据库一般会先「加锁」,所以写数据库,通常是要比读数据库的时间更长的,缓存的写入通常要远远快于数据库的写入,所以在实际中很难出现请求 B 已经更新了数据库并且删除了缓存,请求 A 才更新完缓存的情况。

Cache Aside 策略适合多读写少的场景,不适合写多的场景,因为写入频繁时,缓存回被频繁地清除,这样会对缓存的命中率有一些影响。解决方案是加分布式锁,但是影响写入的性能。

1.2 Read/Write Through (读穿/写穿)策略

应用程序只和缓存交互,不再和数据库交互,而是由缓存和数据库交互,相当于更新数据库的操作由缓存自己代理了。

  1. Read Through 策略

    先查询缓存中数据是否存在,如果存在就直接返回,如果不存在,则有缓存组件负责从数据库中查询数据,并将结果写入到缓存组件中,最后缓存组件将数据返回给应用

  2. Write Through 策略

    当有数据更新的时候,先查询要写入的数据在缓存中是否已经存在:

    • 如果缓存中数据已经存在,则更新缓存中的数据,并且由缓存组件同步到数据库,然后缓存组件告诉应用程序更新完成
    • 如果缓存数据不存在,直接更新数据库,然后返回

Write Through/Write Through 策略的特点是由缓存节点和数据库打交道而不是应用程序自身。开发中少见,因为我们常用的Redis 或者 Memcached 都不支持这种交互方式。在使用本地缓存的时候,可以考虑这种策略。

1.3 Write Back(写回)策略

写回策略在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不是更新数据。而脏块儿只有被再次使用时才会将其中的数据写入到后端存储中。

如果使用 Write Back 策略的话,读的策略也有一些变化了:

  1. 我们在读取缓存时如果发现 缓存命中则直接返回缓存数据

  2. 如果缓存不命中则寻找一个可用的缓存块儿

    如果这个缓存块儿是 「脏」的,就把缓存块儿中之前的数据写入到后端存储中,并且从后端存储加载数据到缓存块儿

    如果不是脏的,则由缓存组件将后端存储中的数据加载到缓存中,最后我们将缓存设置为不是脏的,返回数据就好了。

Write Back(写回)策略也不能应用到我们常用的数据库和缓存的场景中,因为 Redis 并没有异步更新数据库的功能。

Write Back 是计算机体系结构中的设计,比如 CPU 的缓存、操作系统中文件系统的缓存都采用了 Write Back(写回)策略。

Write Back 策略特别适合写多的场景,因为发生写操作的时候, 只需要更新缓存,就立马返回了。比如,写文件的时候,实际上是写入到文件系统的缓存就返回了,并不会写磁盘

2. 数据库和缓存如何保证一致性?

从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案(如果要求强一致性的话,我认为没有必要添加缓存了,直接走数据库)。

一般采用Cache Aside 策略,中文是叫旁路缓存策略。

采用:「先更新数据库 + 再删除缓存」的方案,是可以很大程度上保证数据一致性的。(因为写数据库一般会先「加锁」,所以写数据库,通常是要比读数据库的时间更长的
在这里插入图片描述

为了确保万无一失,还给缓存数据加上了「过期时间」,就算在这期间存在缓存数据不一致,有过期时间来兜底,这样也能达到最终一致

采用 这个方案可以很大程度上避免数据库并发导致的一致性问题,但是如果删除缓存失败,那么数据库是新数据,缓存是旧数据,依然会有问题。

如何保证删除缓存也能成功?

重试机制。

  1. 我们可以引入消息队列,将第二个操作(删除缓存)要操作的数据加入到消息队列,由消费者来操作数据。

    • 如果应用删除缓存失败,可以从消息队列中重新读取数据,然后再次删除缓存,这个就是重试机制。当然,如果重试超过的一定次数,还是没有成功,我们就需要向业务层发送报错信息了。
      如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作,否则就继续重试。
  2. 订阅 MySQL binlog,再操作缓存。

    • 先更新数据库,再删缓存的策略的第一步是更新数据库,那么更新数据库成功,就会产生一条变更日志,记录在 binlog 里。于是我们就可以通过订阅 binlog 日志,拿到具体要操作的数据,然后再执行缓存删除,阿里巴巴开源的 Canal 中间件就是基于这个实现的。

参考:https://zhuanlan.zhihu.com/p/543713664?utm_id=0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值