Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案

文章探讨了Java环境中保证缓存和数据库一致性的问题,提出了四种基础同步策略,包括先更新缓存、先更新数据库、先删除缓存以及推荐的先更新数据库再删除缓存。文章还讨论了各种策略的优缺点,如更新缓存可能导致的性能问题和删除缓存可能引发的未命中情况。随后,文章介绍了同步删除、延时双删、异步监听和可靠消息删除等更高一致性方案,以及多重保障的最终强一致方案,强调了在不同场景下如何选择合适的数据一致性策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导航:

【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析

目录

一、四种基础同步策略

1.1 同步策略

1.2 更新缓存还是删除缓存?

1.2.1 更新缓存的优缺点

1.2.2 删除缓存的优缺点(推荐)

1.3 先操作数据库还是先删除缓存?

1.3.1 先删除缓存再操作数据库的优缺点

1.3.2 先操作数据库再删除缓存的优缺点(推荐)

1.4 最优同步策略:先更新数据库、再删除缓存

二、同步删除+可靠消息方案

三、延时双删:更高一致性方案

四、异步监听+可靠消息删除方案

五、多重保障:最终强一致方案


一、四种基础同步策略

1.1 同步策略

保证缓存和数据库的双写一致性,共有四种同步策略,即先更新缓存再更新数据库、先更新数据库再更新缓存、先删除缓存再更新数据库、先更新数据库再删除缓存。 

  • 先更新缓存再更新数据库:第二步失败缓存库是脏数据
  • 先更新数据库再更新缓存:第二步失败缓存库是旧数据
  • 先删除缓存再更新数据库:第二步失败缓存库是空数据
  • 先更新数据库、再删除缓存(推荐):第二步失败缓存库是旧数据 

1.2 更新缓存还是删除缓存?

1.2.1 更新缓存的优缺点

更新缓存的优点是每次数据变化时都能及时地更新缓存,这样不容易出现查询未命中的情况,但这种操作的消耗很大,如果数据需要经过复杂的计算再写入缓存的话,频繁的更新缓存会影响到服务器的性能。如果是写入数据比较频繁的场景,可能会导致频繁的更新缓存却没有业务来读取该数据。

1.2.2 删除缓存的优缺点(推荐)

删除缓存的优点是操作简单,无论更新的操作复杂与否,都是直接删除缓存中的数据。这种做法的缺点则是,当删除了缓存之后,下一次容易出现未命中的情况,那么这时就需要再次读取数据库。 

那么对比而言,删除缓存无疑是更好的选择。 

1.3 先操作数据库还是先删除缓存?

1.3.1 先删除缓存再操作数据库的优缺点

情况1:数据库和缓存内容不一致

线程1删除缓存后还没有来得及更新数据库时,线程2读缓存,由于缓存中的数据已经被线程1清空了所以线程2需要去数据库读数据,然后把读到的结果保存到缓存中。此时线程1更新更新数据库成功。就会出现数据库和缓存内容不一致。

情况2:缓存击穿,数据库卡死

线程1删除缓存后还没有来得及更新数据库时,来了大量的读请求,由于缓存中没有数据,导致缓存击穿直接将大量请求访问到数据库,导致数据库崩溃。

1.3.2 先操作数据库再删除缓存的优缺点(推荐)

脏数据问题:先操作数据库但删除缓存失败的话,导致缓存库里一直存留着旧数据,而我们数据库里存的是新数据。

解决办法:异步重试机制

出现上述问题的时候,我们一般采用重试机制解决,而为了避免重试机制影响主要业务的执行,一般建议重试机制采用异步的方式执行。当我们采用重试机制之后由于存在并发,先删除缓存依然可能存在缓存中存储了旧的数据,而数据库中存储了新的数据,二者数据不一致的情况。

1.4 最优同步策略:先更新数据库、再删除缓存

所以我们得到结论:先更新数据库、再删除缓存是影响更小的方案。如果第二步出现失败的情况,则可以采用重试机制解决问题。

同步删除方案: 先更新数据库、再删除缓存。适用于不强制要求数据一致性的情景

流程:先更新数据库、再删除缓存。

问题:

  • 并发时脏数据:在查询数据库到写缓存期间其他线程执行了一次更新删除,导致缓存的数据是旧数据
  • 缓存删除失败:删除失败导致缓存库还是旧数据

二、同步删除+可靠消息方案

同步删除+可靠消息删除: 适用于不强制要求数据一致性的情景

流程:先更新数据库、再删除缓存,如果删除失败就发可靠MQ不断重试删除缓存,直到删除成功或重试5次。

问题:MQ多次重试失败,导致长期脏数据。

三、延时双删:更高一致性方案

延时双删方案:比同步删除策略一致性更高的方案。

流程:先删除缓存再更新数据库,大约在数据库从库更新后再删一次。

问题:时间无法控制,不能保证在数据库从库更新后删除缓存。如果在从库更新前删除,用户再在更新前查从库又把脏数据写在缓存里了。

四、异步监听+可靠消息删除方案

异步监听+可靠消息删除:很多大厂正在使用的方案。

流程:

  1. 更新数据库后不做操作;
  2. Canal等组件监听binlog发现有更新时就发可靠MQ删除缓存;
  3. 如果删除缓存失败,就基于手动ack、retry等机制,让消息在有限次数之内不断重试。

优点:

  • 异步删除,性能更高;
  • 可靠消息重试机制,多次删除保证删除成功。

问题:要求canal等binlog抓取组件高可用,如果canal故障,会导致长期脏数据。

五、多重保障:最终强一致方案

多重保障方案:同步删除+ 异步监听+可靠消息删除,缓存时设置过期时间,查询时强制主库查;适合于强制要求数据一致性的情况

  1. 同步删除:先更新数据库、再删除缓存;之后本链路禁止再查该数据,防止没来得及删缓存就又查到旧缓存数据。
  2. Canal监听:Canal等组件监听binlog发现有更新时就发可靠MQ删除缓存;第二重保证删缓存成功;
  3. 延迟消息校验一致性:Canal等组件监听binlog,发延迟MQ,N秒后校验缓存一致性;
  4. 缓存过期时间:每次缓存时设置过期时间;第三重保证删缓存成功;
  5. 强制Redis主库查:以后查缓存时强制从缓存主库查;因为主从同步有延迟,同时不用担心主库压力大,因为分片集群机制。
在分布式系统中,为了确保MySQLRedis之间的数据一致性,我们可以采取多种策略。这些策略包括延时删除缓存重试机制读取binlog异步删除缓存等。 参考资源链接:[Java面试:MySQLRedis一致性策略解析](https://wenku.csdn.net/doc/11iusay893?spm=1055.2569.3001.10343) 首先,来看延时策略。这个方案的实现步骤是这样的:当数据更新操作发生时,系统首先删除Redis中的缓存,然后更新MySQL数据库。在数据库更新完成后,系统会等待一段时间(例如1秒),之后再次尝试删除Redis缓存。这个等待时间的设置是为了让所有在更新数据库之前发起的读请求能够完成,这样可以减少它们读取到旧数据的可能性。不过,如果在第二次删除缓存时失败,或者等待时间设置不当,仍然会出现数据不一致的情况。为了缓解这种情况,可以给缓存设置一个合理的过期时间,让其在过期后自然失效,但这仍然无法完全避免不一致。 接下来是删除缓存重试机制。这个策略是为了弥补延时的不足。在更新MySQL数据库之后,删除缓存操作会尝试进行。如果删除失败,系统会把需要删除缓存键值放入一个消息队列中。后台有一个消费者服务,它会不断地从队列中取出这些键值,并尝试删除缓存。通过这种异步重试的方式,可以有效地提高缓存删除成功的概率。然而,这种方案增加了系统的复杂性,并且需要维护额外的消息队列服务。 最后是读取binlog异步删除缓存策略。这种方法利用了MySQL的binlog来同步MySQLRedis的数据。在数据库更新操作发生时,系统会记录这个操作到binlog中。通过监听binlog的变化,后台服务能够知道何时更新了数据库,并异步删除Redis中相应的缓存。为了确保读取请求不会读到过时的数据,后台服务需要等待足够的时间,直到所有相关操作都完成。这个方案在减少业务入侵的同时,也减少了因直接操作缓存而导致的数据不一致问题,但需要处理binlog的监听解析的复杂性。 每种策略都有其适用场景潜在的风险。延时适用于对一致性要求不是极端严格的场景,删除缓存重试机制适用于可以接受业务复杂度增加的情况,而读取binlog异步删除缓存适用于可以接受异步处理延迟的情况。在实际应用中,往往需要根据系统的具体需求资源消耗情况来选择合适的策略,有时候甚至需要结合多种策略来达到最佳的效果。 通过理解这些策略并根据具体的应用场景灵活应用,可以有效提高分布式系统中数据库缓存之间数据一致性保障。 参考资源链接:[Java面试:MySQLRedis一致性策略解析](https://wenku.csdn.net/doc/11iusay893?spm=1055.2569.3001.10343)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小海绵【vincewm】

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值