缓存与数据库一致性终极指南:从入门到放弃?不,到精通!下

简介: 本文探讨缓存一致性难题,从延时双删到重试机制,分析同步重试、异步重试、消息队列补偿及Binlog监听(Canal)等方案,结合优缺点与适用场景,最终提出根据业务需求权衡一致性与性能,选择合适策略。

上篇文章提到了延时双删仍然会存在删除缓存失败的情况。需要通过重试机制来保障删除缓存的成功率


前情回顾

上回说的小树做了一个缓存的功能,可是出现了数据不一致的情况,经过老大哥的指教,先是想到了旁路缓存的思路,后面还是会存在数据不一致的问题,经过几番优化,又找到了延时双删的策略来减少并发时出现的问题。可是仍然没有解决删除缓存时可能失败的问题,只是提到了可以考虑使用失败重试机制

失败重试的方案

老大哥跟小树说:
失败重试的方法一般分为两类

  • 同步重试
  • 异步重试

同步重试的实现就很简单了,直接在程序中实现,失败后再次删除,达到一定次数后停止重试。缺点也很明显,并发高的时候对接口性能很大。
异步的实现方式就很多了:

  1. 通过线程实现
    每次新增一个线程来进行重试操作。高并发下容易创建太多线程,会出现OOM问题;
    当然你可以通过线程池来管理线程,这样可以避免OOM问题
    但通过线程来进行重试的话,无法保留重试的记录,如果服务器重启,尚未重试成功的数据就会丢失
  2. 重试内容写入表中,通过定时任务执行
  3. 将重试的内容写入消息中间件中,通过消费消息来删除缓存,由消息中间件来保证消息的可靠消费

小树听了上面的三种方式说:第一种和第二种方法太简单了。你还是给我讲讲消息队列的方法吧

消息队列补偿

通过异步解耦,确保缓存删除操作最终成功。

实现步骤

322f7e932e60d4eabf45eb0cda1da5dd_MD5.jpeg

  1. 用户操作数据后,程序先更新数据库,然后删除缓存,成功直接返回,失败则放入消息中间件中
  2. 消费者检测到有消息开始消息消息,执行删除缓存操作,成功后删除这条消息,失败则进行重试,重试达到一定次数后,可根据业务做出处理如写入表中或者直接抛出错误

    优缺点

优点 缺点 适用场景
高可靠性 系统复杂度高 分布式系统
解耦业务 消息可能堆积 订单/支付系统

选型建议

  • 低并发场景:用Redis Stream实现轻量级队列
  • 高并发场景:RocketMQ + 死信队列监控

小树听了上面几种方案,又提问道:如果选上面几种方案的话,我需要改造原来的代码逻辑,有没有不需要改动原来代码就可以完成删除缓存的操作

方案四:Binlog监听(Canal)

通过数据库日志驱动缓存更新,实现业务零侵入。

Canal部署架构

27d8115546fc8f9d3e803fc0cfb7ac58_MD5.jpeg

具体方案如下:

  1. 程序只需要更新数据库就好了,其他的操作完全不关心。
  2. 中间件会伪装为MySQL 的从库,同步订阅binlog日志来获取变更的数据。
  3. binlog订阅者获取变更的数据,然后删除缓存。

优势

  • 完全解耦业务代码
  • 实时性达到毫秒级

    优缺点

优点 缺点 适用场景
业务零侵入 部署复杂 微服务架构
毫秒级同步 需处理DDL事件 数据异构同步

适用场景

  • 微服务架构中多个服务共享缓存
  • 数据异构场景(如ES索引同步)

小树听了总结道:上面这些方案我都理解了,但这些方案好像都只是尽量避免缓存不一致的情况,并没有真正的实现读缓存的时候一定和数据库保持一致
老大哥点了点头,说:是的,数据库追求数据的准确性和持久性,而缓存则追求极致的响应速度。大部分应用缓存的场景都是可以允许短暂的数据不一致的情况的,也就是最终一致性。如果追求强一致性,不可避免的就要牺牲一部分的响应性能。

常见的方案就是加锁,加锁方式又分为乐观锁和悲伤锁两种,这两个概念之前的文章有介绍过,这里简单总结一下各自的特点,就不详细介绍了

乐观锁:版本号控制

通过数据版本校验,实现精准更新。

额外成本

  • 存储开销增加5%-10%
  • 所有读写操作需校验版本号

回报

  • 缓存命中率提升40%
  • 彻底解决并发更新导致的脏读

终极大招:分布式锁

通过互斥锁确保同一时刻只有一个写操作。

性能数据

  • 吞吐量下降60%(实测)
  • 平均响应时间从20ms → 80ms

使用铁律

  • 仅用于库存扣减等核心场景
  • 必须设置锁超时时间!

最后抉择:一表帮你选型

场景特征 推荐方案 一致性强度 性能影响
读多写少 Cache-Aside 最终一致 无影响
高并发写入 双删+消息队列 最终一致+ 中等
金融/交易场景 分布式锁+版本号 强一致

选择方案时,记得问自己一句你的业务真的需要强一致性吗?

相关文章
|
6月前
|
消息中间件 canal 缓存
缓存与数据库一致性终极指南:从入门到放弃?不,到精通!上
凌晨被投诉惊醒?缓存与数据库不一致是常见难题。本文详解五大解决方案:旁路缓存、双删策略、消息队列补偿、Binlog监听与版本号控制,结合场景分析一致性、性能与复杂度的权衡,助你选型不踩坑。
|
SQL 搜索推荐 测试技术
【Havenask实践篇】完整的性能测试
Havenask是阿里巴巴智能引擎事业部自研的开源高性能搜索引擎,深度支持了包括淘宝、天猫、菜鸟、高德、饿了么在内几乎整个阿里的搜索业务。性能测试的目的在于评估搜索引擎在各种负载和条件下的响应速度、稳定性。通过模拟不同的用户行为和查询模式,我们可以揭示潜在的瓶颈、优化索引策略、调整系统配置,并确保Havenask在用户数量激增或数据量剧增时仍能保持稳定运行。本文举例对Havenask进行召回性能测试的一个简单场景,在搭建好Havenask服务并写入数据后,使用wrk对Havenask进行压测,查看QPS和查询耗时等性能指标。
66409 6
|
SQL 存储 OLAP
阿里CCO基于Hologres的亿级明细BI探索分析实践
阿里CCO基于Hologres的亿级明细BI探索分析实践。
1778 0
阿里CCO基于Hologres的亿级明细BI探索分析实践
|
6月前
|
JSON 缓存 测试技术
程序出错瞎找?教你写“会说话”的错误日志,秒定位原因
错误日志是排查问题的“导航地图”。本文详解错误三大来源:参数非法、交互故障、逻辑疏漏,并分享写好日志的6大原则——完整、具体、直接、集成经验、格式统一、突出关键字,助你快速定位问题,提升系统可维护性。
458 0
|
6月前
|
人工智能 Java API
Java AI智能体实战:使用LangChain4j构建能使用工具的AI助手
随着AI技术的发展,AI智能体(Agent)能够通过使用工具来执行复杂任务,从而大幅扩展其能力边界。本文介绍如何在Java中使用LangChain4j框架构建一个能够使用外部工具的AI智能体。我们将通过一个具体示例——一个能获取天气信息和执行数学计算的AI助手,详细讲解如何定义工具、创建智能体并处理执行流程。本文包含完整的代码示例和架构说明,帮助Java开发者快速上手AI智能体的开发。
2096 8
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
12月前
|
消息中间件 缓存 NoSQL
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
缓存与数据库的一致性方案,Redis与Mysql一致性方案,大厂P8的终极方案(图解+秒懂+史上最全)
|
10月前
|
JSON 安全 数据可视化
Elasticsearch(es)在Windows系统上的安装与部署(含Kibana)
Kibana 是 Elastic Stack(原 ELK Stack)中的核心数据可视化工具,主要与 Elasticsearch 配合使用,提供强大的数据探索、分析和展示功能。elasticsearch安装在windows上一般是zip文件,解压到对应目录。文件,elasticsearch8.x以上版本是自动开启安全认证的。kibana安装在windows上一般是zip文件,解压到对应目录。elasticsearch的默认端口是9200,访问。默认用户是elastic,密码需要重置。
5050 0
|
负载均衡 监控 Dubbo
Dubbo 原理和机制详解(非常全面)
本文详细解析了 Dubbo 的核心功能、组件、架构设计及调用流程,涵盖远程方法调用、智能容错、负载均衡、服务注册与发现等内容。欢迎留言交流。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Dubbo 原理和机制详解(非常全面)

热门文章

最新文章