当前位置: 首页 > news >正文

怎样去保证 Redis 缓存与数据库双写一致性?

解决方案

那么我们这里列出来所有策略,并且讨论他们优劣性。

  1. 先更新数据库,后更新缓存
  2. 先更新数据库,后删除缓存
  3. 先更新缓存,后更新数据库
  4. 先删除缓存,后更新数据库

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

        这种方法是不推荐使用的,因为在更新缓存那一步有的业务需求缓存中的值并不是从数据库查,而是需要一系列计算而拿到缓存值,那这时候更新缓存的代价是非常大的。当有大量对数据库进行写的请求时,读的并不多,写一次请求就要更新一下缓存,那性能损耗真的很大,因为Redis是针对内存的。

        比如:当数据库有个值为100数值,我们有十个请求是要对其每次减五,这个期间完全没进行读的操作,那么先更新数据库,那会有十个请求对缓存更新,从而产生大量的冷数据,当不更新缓存而失去删除缓存,那么有读请求时只会更新一次。

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

这种方法不需要我们考虑了吧,和第一种方法是一样的。

先删除缓存,后更新数据库

该方法也会有问题,具体出现的原因:

这时来了两个请求,请求 A(更新) 和请求 B(查询)

  1. 请求 A 会先删除 Redis 中的数据,然后去数据库进行更新操作
  2. 此时请求 B 看到 Redis 中的数据时空的,会去数据库中查询该值,补录到 Redis 中
  3. 但是此时请求 A 并没有更新成功,或者事务还未提交

那么这时候就会产生数据库和 Redis 数据不一致的问题。如何解决:其实最简单的解决办法就是延时双删的策略。

上述的保证事务提交完以后再进行删除缓存还有一个问题,就是如果你使用的是 Mysql 的读写分离的架构的话,那么其实主从同步之间也会有时间差。

此时来了两个请求,请求 A(更新) 和请求 B(查询)

  1. 请求 A 更新操作,删除了 Redis
  2. 请求主库进行更新操作,主库与从库进行同步数据的操作
  3. 请 B 查询操作,发现 Redis 中没有数据
  4. 去从库中拿去数据
  5. 此时同步数据还未完成,拿到的数据是旧数据

解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,就强制将其指向主库进行查询。

先更新数据库,后删除缓存

        这一种方法也会出现问题,当更新数据库成功了,但在删除缓存的阶段出错了没有删除成功,那此时再读取缓存的时候每次都是错误的数据了。

解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑如下:

  1. 请求 A 先对数据库进行更新操作
  2. 在对 Redis 进行删除操作的时候发现报错,删除失败
  3. 此时将Redis 的 key 作为消息体发送到消息队列中
  4. 系统接收到消息队列发送的消息后再次对 Redis 进行删除操作

但是这个方案会有一个缺点就是会对业务代码造成大量的侵入,深深的耦合在一起,所以这时会有一个优化的方案,我们知道对 Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作。

总结        

        这些方法都有利弊,如在第二种先删除缓存,后更新数据库这个方法最后讨论了要更新 Redis 的时候强制走主库查询就能解决问题,那么这样的操作会对业务代码进行大量的侵入,但不需要增加的系统,不需要增加整体的服务的复杂度。最后一种方法我们最后讨论了利用订阅 binlog 日志进行搭建独立系统操作 Redis,这样的缺点其实就是增加了系统复杂度。

        所有的选择都需要我们对业务处理的评估来进行选择,没有一种技术是对所有业务通用的。没有最好的技术,只有最适合我们的。

相关文章:

  • Debian GNU/Linux 安装docker与docker compose
  • 将yolov5s部署到安卓上实战经验总结
  • 【研发日记】Matlab/Simulink技能解锁(五)——Simulink布线技巧
  • 大话设计模式之原型模式
  • DevSecOps平台架构系列-微软云Azure DevSecOps平台架构
  • 主流后端开发语言:JAVA、C、C++、GO、PYTHON对比
  • 如何系统的学习 C#
  • C#使用iText7画发票PDF——字体与自定义颜色
  • 上位机图像处理和嵌入式模块部署(qmacvisual透视变换)
  • 优雅的使用ChromeDriver
  • 论文《Exploring to Prompt for Vision-Language Models》阅读
  • Linux小程序: 手写自己的shell
  • LabVIEW比例流量阀自动测试系统
  • Eclipse+Java+Swing实现斗地主游戏
  • 散热风扇220v交流12v直流12038轴流风机配电箱机柜散热风扇15050
  • 时间复杂度分析经典问题——最大子序列和
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • IndexedDB
  • Javascript Math对象和Date对象常用方法详解
  • Javascript编码规范
  • java概述
  • MySQL用户中的%到底包不包括localhost?
  • python学习笔记-类对象的信息
  • Tornado学习笔记(1)
  • Vue全家桶实现一个Web App
  • 阿里研究院入选中国企业智库系统影响力榜
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 前端技术周刊 2019-02-11 Serverless
  • 前端性能优化——回流与重绘
  • 如何选择开源的机器学习框架?
  • 设计模式(12)迭代器模式(讲解+应用)
  • 深度学习入门:10门免费线上课程推荐
  • 深入浅出Node.js
  • 思维导图—你不知道的JavaScript中卷
  • 大数据全解:定义、价值及挑战
  • ![CDATA[ ]] 是什么东东
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (1)bark-ml
  • (13)Hive调优——动态分区导致的小文件问题
  • (3)nginx 配置(nginx.conf)
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (LeetCode) T14. Longest Common Prefix
  • (zhuan) 一些RL的文献(及笔记)
  • (论文阅读30/100)Convolutional Pose Machines
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (四) Graphivz 颜色选择
  • (转)【Hibernate总结系列】使用举例
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • .chm格式文件如何阅读
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .NET的微型Web框架 Nancy
  • .Net组件程序设计之线程、并发管理(一)
  • @JsonFormat与@DateTimeFormat注解的使用
  • @JsonSerialize注解的使用