缓存一致性方案比较

在高并发的系统中,使用缓存来提升查询性能是十分必要的。在关系型数据库中(如MySQL)对于高并发处理能力并不是很强,而缓存由于在内存中处理,并不需要磁盘IO,所以非常适合高并发处理。

缓存虽然能显著提升查询效率,但是更新缓存和更新数据库不可能在一个事务中进行,所以就很难保证缓存的一致性。下面我们看看缓存更新都有哪些策略,以及对应的优缺点。

定时刷新

在这里插入图片描述

使用定时刷新策略,写入数据库和写入缓存是独立进行的,写入数据库后,需要单独使用定时任务去刷新缓存。这种方式会导致在较长的一段时间内,缓存与数据库数据不一致,而且不管数据是否有更新,都会定时去刷新缓存,效率低下。此方案适用于系统配置模型、或者归档的数据报表等数据不经常改变,或者数据改变对业务影响不是很大的场景。

先更新数据库,在更新缓存

这是最直观想到的,也是最简单的一种刷新缓存策略。

在这里插入图片描述
使用这种双写的方案,只要在数据库更新成功,立即更新缓存,但是在并发场景下,容易造成数据不一致。例如线程A和B同时来更新数据库,但更新数据库和更新缓存不能保证原子性,可能出现如下情况:

在这里插入图片描述
这中方案存下一下弊端:
1、数据库和缓存中的数据不一致,从而缓存数据成了脏数据。
2、对于写多读少的操作,由于频繁的刷新缓存,而缓存的数据根本没有被读取过,增加写负担和服务资源浪费。

先删缓存再更新数据库

由于双写存在的问题,那我们考虑先删除缓存,再更新数据库,并且不主动更新缓存,等到再次查询的时候写入缓存。这样在更新数据库前,由于缓存中的数据被删除,这是请求查询缓存中不存在,再去查询数据库,然后将数据放到缓存中,缓存就不会频繁的被刷新了。
在这里插入图片描述
这种方案是否完美了呢?同样有两个请求,请求A去写数据,请求B去读数据,就会存一下一下问题:
在这里插入图片描述
这种情况下,脏数据又进入缓存了,如果没有设置过期时间,那么在下一次写入数据之前,脏数据会一直存在。针对这种脏数据的出现,我们决定在写入数据后,增加一点延迟,再删除一次数据,于是就有了下面的延迟双删策略。

延迟双删

在这里插入图片描述

延迟双删策略,能够很好的解决之前我们应对并发引起的数据不一致的情况。那是不是延迟双删就没有问题了呢?

我们再来看一个场景,针对做了读写分离场景,使用双删会出现什么问题呢?
在这里插入图片描述
糟糕,由于主从同步有延迟,又导致数据不一致了。再从性能角度查看,由于二次删除需要延迟,只能做成异步。那异步线程执行删除就会出现新的问题,如果异步线程删除失败了,那么旧数据就不会被删除,数据又不一致了。在此基础上,考虑使用可靠消息队列保证删除缓存业务百分百执行。

队列删除缓存

在这里插入图片描述
我们把数据删除更新到数据库中,把删除缓存的消息加入到队列中,如果消息投递失败,就再次加入到队列执行,直到成功为止。

这样,我们就能够有效保证数据库和缓存数据不一致了,不管是读写分离还是其他情况,只要消息队列能够保证安全,那么缓存就一定会被刷新。(在发生删除消息前,Master需要等Slave节点发生ACK,否则在消息消费了,单主从并未完成同步,还是去从库读取旧值,可以参考下MySQL半同步复制)

根据这个方案,还可以进一步优化。因为我们是基于业务代码进行缓存刷新的,导致业务代码和刷新缓存耦合度很高。那有没有办法能将刷新缓存抽取出来,不基于业务代码执行呢?

binlog订阅删除缓存

我们都知道,MySQL主从数据同步,或者canal进行数据同步都是基于binlog日志文件进行同步的。

在这里插入图片描述
使用binlog订阅,我们就完美的将业务代码和缓存刷新独立开了。代码量小了,也方便维护了,程序员不需要关系是否需要刷新缓存。

当然,实战中,我们还有很多不同的业务场景,可能需要的数据不一致同步方案也不同,你在工作中都是要到哪些缓存同步方案呢?

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值