如何保证Redis和数据库数据一致性

        缓存可以提升性能,减轻数据库压力,在获取这部分好处的同时,它却带来了一些新的问题,缓存和数据库之间的数据一致性问题。

想必大家在工作中只要用了咱们缓存势必就会遇到过此类问题

首先我们来看看一致性:

  • 强一致性:任何一次读都能读到某个数据的最近一次写的数据。
  • 弱一致性:数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。

1.读取数据

  1. 当应用程序需要从数据库读取数据时,先检查缓存数据是否命中。
  2. 如果缓存未命中,则查询数据库获取数据,同时将数据写到缓存中,以便后续读取相同数据会命中缓存,最后再把数据返回给调用者。
  3. 如果缓存命中,直接返回。

        单独的只读取数据场景是不会出现不一致。 只有读和写一起才会出现 , 那我们再来说下写数据的场景

问题:如果数据库中的某条数据放入缓存后,又马上被更新了,那我们应该如何更新缓存

2.写数据

当我们对数据进行修改的时候,到底是先删缓存,还是先写数据库?

  • 先更新缓存再更新数据库
  • 先删除缓存再更新数据库
  • 先更新数据库再更新缓存
  • 先更新数据库再删除缓存

无非就是缓存用更新或用删除?推荐直接删除

        为什么不更新?而直接删, 因为缓存的更新成本更高(因为你写入数据库的值,很多情况并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么,每次写入数据库后,都再次计算写入缓存的值,无疑是浪费性能的。显然,删除缓存更为适合。)删除缓存操作简单,副作用只是增加了一次 chache miss,建议大家使用该策略。

先操作数据库还是先操作缓存?

2.先操作缓存

2.1先更新缓存,再更新数据库

缺点:如果先更新缓存成功,在更新数据库的时候失败,这时候会导致数据不一致;缓存的作用是不是临时将我们数据保存在内存,便于提高查询速度;但是如果某条数据在数据库中都不存在,缓存这种数据没有一点意义

2.2.先删除缓存,再更新数据库

缺点:高并发场景下,如果多个线程同时执行更新数据库再写缓存操作可能会出现数据库是新值,而缓存中是旧值

2.3.先删缓存再删数据库 

        先删缓存再删数据库:在多线程环境下,当一个线程把缓存删掉之后,另一个线程读缓存,读不到缓存就会直接读库,读到数据后就会更新缓存,先前的线程呢,才更新数据库,会造成缓存脏读的情况,很容易产生缓存脏读。

而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。

3.先操作数据库

3.1.先更新数据库,再更新缓存

优点:可以解决先更新缓存,再更新数据库带来的假数据问题

缺点:高并发场景下,如果多个线程同时执行更新数据库再写缓存操作可能会出现数据库是新值,而缓存中是旧值

3.2.先更新数据库,再删除缓存

        在高可用的系统系统里面,我们追求数据最终一致性的话,我们可以考虑先更新数据库,再去删除缓存

        也算是常用的方案,这里介绍一下,这个叫 Cache Aside Pattern。如果先更新数据库,再删除缓存,那么就会出现更新数据库之前有瞬间数据不是很及时。

        同时,如果在更新之前,缓存刚好失效了,读客户端有可能读到旧值,然后在写客户端删除结束后再次设置了旧值,非常巧合的情况。

        有 2 个前提条件:缓存在写之前的时候失效,同时,在写客户度删除操作结束后,放置旧数据 — 也就是读比写慢。设置有的写操作还会锁表。

这个很难出现,但是如果出现了怎么办?使用双删!!!

3.3先删数据库再删缓存 

先删数据库再删缓存在多线程情况下,当一个线程删除数据库,另一个线程读取缓存数据,读到的是缓存的数据,当先前一个线程删完数据库后就会更新缓存,这是缓存就正常了,产生了一次脏读。 

5.解决

5.1.强一致性?

在强一致性系统中,通过2PC、Paxos或分布式锁保持一致性可能会成为影响系统吞吐量、响应时间和可伸缩性的昂贵开销, 因此通常采用一种相当宽松的一致性方法,称为最终一致性。

5.2.最终一致性:延时双删

关键:间隔一段时间再删除是为了保证并发读请求写入的旧值最终能够被第二次删除删除掉

缺点:延时双删可能对我们性能要求方面不能有太高的要求

注意:我们需要自行评估项目的读数据业务逻辑的耗时(即线程二从数据库读取数据 写入缓存完成), 防止线程二覆盖掉新数据

如果第二次删除缓存失败怎么办?

4.为了防止删除缓存失败,可以进行重试机制

  • 同步重试,如果并发量高的时候可能会影响接口性能
  • 异步重试:
    • 创建单独的一个线程,进行重试;但是在高并发的场景下,可能会因为创建线程太多,导致OOM
    • 交给线程池处理;但是如果服务重启,会导致数据丢失
    • 重试数据写入表,通过定时任务重试(可以保证数据不丢失,但是对于实时性要求较高的该场景不太适用)
    • 利用MQ消息中间件进行重试,在消费者中处理

  • 订阅mysql的binlong,在订阅者中,如果发现更新数据请求,则删除响应的缓存,比如使用canal中间件;为了保证删除缓存成功,可以增加MQ


6.总结 

缓存策略的最佳实践是 **Cache Aside Pattern。**分别分为读缓存最佳实践和写缓存最佳实践。

读缓存最佳实践:先读缓存,命中则返回;未命中则查询数据库,再写到数据库。

写缓存最佳实践:

  • 先写数据库,再操作缓存;
  • 直接删除缓存,而不是修改,因为缓存的更新成本很高,删除缓存操作简单,副作用只是增加了一次 chache miss,建议大家使用该策略。

在以上最佳实践下,为了尽可能保证缓存与数据库的一致性,我们可以采用延迟双删。

防止删除失败,我们采用异步重试机制保证能正确删除,异步机制我们可以发送删除消息到 mq 消息中间件,或者利用 canal 订阅 MySQL binlog 日志监听写请求删除对应缓存。

那么,如果我非要保证绝对一致性怎么办,先给出结论:

没有办法做到绝对的一致性,这是由 CAP 理论决定的,缓存系统适用的场景就是非强一致性的场景,所以它属于 CAP 中的 AP。

所以,我们得委曲求全,可以去做到 BASE 理论中说的最终一致性

其实一旦在方案中使用了缓存,那往往也就意味着我们放弃了数据的强一致性,但这也意味着我们的系统在性能上能够得到一些提升。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/744661.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端实现生成图片并批量下载,下载成果物是zip包

简介 项目上有个需求,需要根据表单填写一些信息,来生成定制的二维码图片,并且支持批量下载二维码图片。 之前的实现方式是直接后端生成二维码图片,点击下载时后端直接返回一个zip包即可。但是项目经理说后端实现方式每次改个东西…

python基础——列表【创建,下标索引,常见操作方法】

📝前言: 这篇文章主要讲解一下python中常见的数据容器之一——列表 本文主要讲解列表的创建以及我们常用的列表操作方法 🎬个人简介:努力学习ing 📋个人专栏:C语言入门基础以及python入门基础 &#x1f380…

泰迪智能科技3月线上培训计划

有学习意向可到 泰迪智能科技官网 咨询了解

Visual Basic6.0零基础教学(3)—焦点概念和深入学习属性

焦点概念和深入学习属性 文章目录 焦点概念和深入学习属性前言一、什么是焦点(Focus)?焦点的特点 二、窗体属性一、窗体的结构二、窗体的属性三、事件四、方法 一.控件属性一. 标签 Label二.文本框 TextBox2.常用事件 三.命令按钮事件 总结 前言 今天我们来继续学习VB中的属性…

Java全系工程源码加密,防止反编译

一、前言 在大约2015年左右,由于公司产品序列逐渐增加,涉及到N多项目部署交付,为了有效防止产品被滥用,私自部署,当时技术中心决定开发一套统一的授权认证管理系统,对公司所有产品序列进行 License 控制。…

Kotlin:为什么创建类不能被继承

一、为什么创建类不能被继承 class或data class 默认情况下,Kotlin 类是最终(final)的:它们不能被继承。 示例:data class PsersonBean 反编译data class PsersonBean 生成 public final class PsersonBean 示例&…

材料科学类3区SCI,仅13天超快上线见刊,国人友好!!

录用案例 JCR3区材料类SCI (3.31截稿) 【期刊简介】IF:3.0-4.0,JCR3区,中科院4区; 【检索情况】SCI在检; 【征稿领域】低温环境下新型生物降解材料的开发相关或结合研究均可; 【案例分享】重要时间节点…

伪分布式Spark集群搭建

一、软件环境 软 件 版 本 安 装 包 VMware虚拟机 16 VMware-workstation-full-16.2.2-19200509.exe SSH连接工具 FinalShell Linux OS CentOS7.5 CentOS-7.5-x86_64-DVD-1804.iso JDK 1.8 jdk-8u161-linux-x64.tar.gz Spark 3.2.1 spark-3.2.1-bin-…

PostgreSQL YUM安装

docker中的centos7中安装 选择对应的版本然后在容器中的centos7中执行下面命令 但是启动容器的时候需要注意 开启端口映射开启特权模式启动init进程 docker run -itd --name centos-postgresql -p 5433:5432 --privilegedtrue centos:centos7 /usr/sbin/init 启动然后进入后先…

java SSM在线学习网站系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM在线学习网站系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用…

【基于HTML5的网页设计及应用】——改变文字和背景颜色

🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL&#xff1a…

有手就会Python自定义模块使用

1.自定义模块 自定义模块一般是在项目中根据自己的需求进行的封装 项目中自定义了额一个模块,module.py name "张三" age 23 weight 160 height 187 def test(): print("测试的方法") def demo(): print("天使的眼泪") …

2024年Twitter上最值得关注的26名顶级程序员

2023年7月23日,在Twitter发布17年后,马斯克在Twitter上表示,是时候将该平台更名为X了。 对很多人来说,Twitter是一个分享他们对社会新闻或人生重大事件的想法的地方,这里也是紧跟文化潮流、获取全球最新消息的最佳方式…

如何更简捷地在 Java 中进行函数式编程

public static void findNemo(List names) { boolean found false; for(String name : names) { if(name.equals(“Nemo”)) { found true; break; } } if(found) System.out.println(“Found Nemo”); else System.out.println(“Sorry, Nemo not found”); } …

392.判断子序列

题目:给定字符串s和t,判断s是否为t 的子序列。 字符串的一个子序列是原始字符串删除一些字符而不改变剩余字符相对位置形成的新字符串。 解题思路:s是否是 t 的子序列,因此只要能找到任意一种 s 在 t 中出现的方式,即…

代码随想录day19(1)二叉树:完全二叉树节点个数(leetcode222)

题目要求:求一个完全二叉树的节点个数 思路:首先完全二叉树可以用普通二叉树的方法来求,但是需要遍历所有的节点。 但是对于完全二叉树来说,只有最底层右侧的节点可能没满,其余每层节点都达到了最大值。所以我们可以…

Nwatch在stm32上的移植

目录 Nwatch在stm32上的移植前言实验目的移植game1_task任务相关代码片段结果本文中使用的工程 Nwatch在stm32上的移植 本文目标:Nwatch在stm32上的移植 按照本文的描述,应该可以跑通实验并举一反三。 先决条件:装有编译和集成的开发环境&…

Linux进程状态

目录 1.R运行状态(running) 2.S睡眠状态(sleep) 3.T或t状态(stopped 或 tracing stop) 4.Z状态(zombie)(僵尸进程) 1.R运行状态(running&…

Self-supervised Contextual Keyword and Keyphrase Retrieval with Self-Labelling

文章目录 题目摘要方法数据集实验 题目 通过自我标记进行自我监督的上下文关键字和关键词短语检索 论文地址:https://www.preprints.org/manuscript/201908.0073/v1 项目地址:https://github.com/naister/Keyword-OpenSource-Data 摘要 在本文中&#x…

反向传播 — 简单解释

一、说明 关于反向传播,我有一个精雕细刻的案例计划,但是实现了一半,目前没有顾得上继续充实,就拿论文的叙述这里先起个头,我后面将修改和促进此文的表述质量。 二、生物神经元 大脑是一个由大约100亿个神经元组成的复…