10 缓存双写一致性之更新策略探讨

news2024/11/16 17:55:07

什么是缓存双写一致性

  • 如果redis中有数据:需要和数据库中的值相同
  • 如果redis中无数据:数据库中的值要是最新值

缓存按照操作来分,有细分2种

  • 只读缓存
  • 读写缓存
    • 同步直写策略:写缓存时也同步写数据库,缓存和数据库中的数据⼀致;
    • 对于读写缓存来说,要想保证缓存和数据库中的数据⼀致,就要采⽤同步直写策略

数据库和缓存一致性的几种更新策略

  • 挂牌报错,凌晨升级:单线程,这样重量级的数据操作最好不要多线程
  • 目的:总之,我们要达到最终一致性!
    • 给缓存设置过期时间,是保证最终一致性的解决方案。
    • 我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记以mysql的数据库写入库为准。
    • 上述方案和后续落地案例是调研后的主流+成熟的做法,但是考虑到各个公司业务系统的差距,不是100%绝对正确,不保证绝对适配全部情况,请同学们自行酌情选择打法,合适自己的最好。

我们讨论3种更新策略

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

  • 先更新mysql的某商品的库存,当前商品的库存是100,更新为99个。
  • 先更新mysql修改为99成功,然后更新redis。
  • 此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100 。
  • 上述发生,会让数据库里面和缓存redis里面数据不一致,读到脏数据

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

异常问题
  1. A线程先成功删除了redis里面的数据,然后去更新mysql,此时mysql正在更新中,还没有结束。(比如网络延时)B突然出现要来读取缓存数据。
    在这里插入图片描述
  2. 此时redis里面的数据是空的,B线程来读取,先去读redis里数据(已经被A线程delete掉了),此处出来2个问题:
    • B从mysql获得了旧值:B线程发现redis里没有(缓存缺失)马上去mysql里面读取,从数据库里面读取来的是旧值。
    • B会把获得的旧值写回redis :获得旧值数据后返回前台并回写进redis(刚被A线程删除的旧数据有极大可能又被写回了)。
    • 在这里插入图片描述
  3. A线程更新完mysql,发现redis里面的缓存是脏数据
    • 两个并发操作,一个是更新操作,另一个是查询操作,A更新操作删除缓存后,B查询操作没有命中缓存,B先把老数据读出来后放到缓存中,然后A更新操作更新了数据库。
    • 于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了
总结流程
  1. 请求A进行写操作,删除缓存后,工作正在进行中…A还么有彻底更新完
  2. 请求B开工,查询redis发现缓存不存在
  3. 请求B继续,去数据库查询得到了myslq中的旧值
  4. 请求B将旧值写入redis缓存
  5. 请求A将新值写入mysql数据库
    上述情况就会导致不一致的情形出现。
    在这里插入图片描述
  • 如果数据库更新失败,导致B线程请求再次访问缓存时,发现redis里面没数据,缓存缺失,再去读取mysql时,从数据库中读取到旧值
解决方案
复习阿里内部缓存击穿的方案
  • 多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。
  • 其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。
    后面的线程进来发现已经有缓存了,就直接走缓存。
    在这里插入图片描述
采用延时双删策略

在这里插入图片描述

  • 加上sleep的这段时间,就是为了让线程B能够先从数据库读取数据,再把确实的数据写入缓存,然后,线程A再进行删除。所以,线程A sleep的时间,就需要大于线程B读取数据再写入缓存的时间。
  • 这样一来,其他线程读取数据时,会发现缓存缺失,所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后,延迟一段时间再次进行删除,所以我们也把他叫做“延迟双删”。
双删方案面试题
  • 这个删除该休眠多久呢?

    • 线程Asleep的时间,需要大于线程B读取数据再写入缓存的时间。
    • 在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。
    • 这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。
  • 当前演示的效果是mysql单机,如果mysql主从读写分离架构如何?

    • 请求A进行写操作,删除缓存
    • 请求A将数据写入数据库了,
    • 请求B查询缓存发现,缓存没有值
    • 请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
    • 请求B将旧值写入缓存
    • 数据库完成主从同步,从库变为新值 上述情形,就是数据不一致的原因。还是使用双删延时策略
    • 只是,睡眠时间修改为在主从同步的延时时间基础上,加几百ms
  • 这种同步淘汰策略,吞吐量降低怎么办?

    • 在这里插入图片描述

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

异常问题

在这里插入图片描述

业务指导思想
  • 老外论文
  • 知名社交网站facebook也在论文《Scaling Memcache at Facebook》中提出
  • 我们上面的canal也是类似的思想:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。
解决方案

在这里插入图片描述

  1. 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。
  2. 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
  3. 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
  4. 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。

总结

方案2和方案3用那个?利弊如何

  • 在大多数业务场景下,我们会把Redis作为只读缓存使用。假如定位是只读缓存来说,理论上我们既可以先删除缓存值再更新数据库,也可以先更新数据库再删除缓存,但是没有完美方案,两害相衡趋其轻的原则
  • 个人建议是,优先使用先更新数据库,再删除缓存的方案。理由如下:
    • 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力,严重导致打满mysql。
    • 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。
  • 多补充一句:如果使用先更新数据库,再删除缓存的方案:如果业务层要求必须读取一致性的数据,那么我们就需要在更新数据库时,先在Redis缓存客户端暂存并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性。
    在这里插入图片描述

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

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

相关文章

如何移动下载文件夹到另一个盘?

下载文件夹占用了越来越多的C盘可用空间?本教程将教你如何安全易行地将下载文件夹移动到其他驱动器,以便你可以释放更多的C盘空间。 关于下载文件夹 从网站下载程序后它们会被存储在哪里?一般来说,当你从互联…

基于C++实现的智慧农业移动巡检系统设计(附源码)

Overview 项目源码 https://download.csdn.net/download/DeepLearning_/87863659 此项目开始于2023年2月7日,项目内容为一种AGV图形化操作系统,采用ROS2GO开发,开发环境为Ubuntu18.04、ROS melodic、Qt5.9.9,该项目作为23年挑战杯…

js函数this指向

目录 this的绑定规则  绑定一:默认绑定; ​ 绑定二:隐式绑定; ​ 绑定三:显式绑定; 通过call或者apply绑定this对象  绑定四:new绑定; 内置函数的绑定 this绑定规则的…

给电脑重装系统的时间需要多久才能装好

在进行电脑重装系统时,如果遇到系统安装时间过长的情况,可能会引起用户的困惑和不安。本文将介绍一些常见的原因和解决方法,以帮助您理解并应对系统安装时间过长的情况。 ​工具/原料: 系统版本:Windows 10 专业版 品…

《Java并发编程实战》课程笔记(九)

Semaphore:如何快速实现一个限流器? 信号量模型 信号量模型还是很简单的,可以简单概括为:一个计数器,一个等待队列,三个方法。 在信号量模型里,计数器和等待队列对外是透明的,所以…

chatgpt赋能python:Python图片大小设置的SEO指南

Python 图片大小设置的SEO指南 在网站设计和开发中,图片大小通常是一个重要的问题。合适的图片大小可以极大地影响用户体验和搜索引擎优化(SEO)结果。Python是一种广泛使用的编程语言,可以用来控制和设置图片大小。在本文中&…

BUUCTF MD5

密文: e00cf25ad42683b3df678c61f42c6bda 简述: 一般MD5值是32位由数字“0-9”和字母“a-f”所组成的字符串,字母大小写统一;如果出现这个范围以外的字符说明这可能是个错误的md5值,就没必要再拿去解密了。 特征&…

SQL-DDL操作数据库、表

SQL-DDL操作数据库、表 1 DDL:操作数据库 1.1 查询数据库 查询所有的数据库 SHOW DATABASES; show databases;1.2 创建数据库 创建数据库 CREATE DATABASE 数据库名称; create database 数据库名称;创建数据库(判断,如果不存在则创建) CREATE DATABASE IF NOT…

SyntaxError:Unexpected end of JSON input while parsing near xxxxx 报错及解决

环境:Node 12.21.0、npm 6.14.11 (其他版本也会出现这样的问题) 找到报错日志并进行查看: less /Users/roc/.npm/_logs/2023-06-05T02_23_51_747Z-debug.log报错信息如下: 19067 verbose stack SyntaxError: Unexp…

【遇到的问题】JAVA应用程序处于安全原因被阻止。

遇到的问题: 直入正题,远程服务器用JAVA连接KVM报以下错(如图)。 应用程序处于安全原因被阻止 无法验证证书 将不执行该应用程序 名称:Java viewer 发行者:ATEN 位置:https://192.168.210:443 原因: 通过…

vue3实现高德地图多点标注(so easy)

vue3实现高德地图多点标注(so easy) 前言思路清晰,抽丝剥茧必要的准备工作最简单的部分处理数据之前(最关键的思路)效果完整代码 前言 非常感谢你能打开这篇博客,我想你一定是遇到了地图多点标注有关的问题…

采购管理系统对企业有什么作用?原来用零代码搭建如此便捷

什么是采购管理系统? 采购管理系统是一种企业内部管理软件,用于协调和管理企业的采购过程。它涵盖了采购计划、询价、比价、采购订单、采购合同、采购收货、发票等一系列采购环节,以及与供应商的信息和交流。其主要目的是:优化采…

M12圆形连接器公母对接带线3PIN4PIN

随着工业自动化的发展,M12圆形连接器公母对接带线3PIN4PIN作为一种重要的连接器件,被广泛应用于各种工业设备中。本文将详细介绍M12连接器的特点以及应用场景,为大家解答M12连接器的相关问题。 M12连接器主要由连接器头、插座和电缆组成&…

【Web网站服务】Nginx Rewrite重写模块

Nginx Rewrite 一、常用的Nginx 正则表达式二、location 匹配的范围2.1location实验 三、rewrite模块3.1rewrite跳转3.2rewrite执行顺3.3flag标记说明3.4rewrite中常用的全局变量3.5rewrite实验3.5.1 基于域名的跳转3.5.2基于客户端IP访问跳转3.5.3基于旧域名跳转到新域名后面加…

三、HAL_无源蜂鸣器的驱动

1、开发环境 (1)KeilMDK:V5.38.0.0 (2)STM32CubeMX:V6.8.1 (3)MCU:STM32F407ZGT6 2、无源蜂鸣器简介 无源蜂鸣器内部没有振荡源,需要采用一定频率的方波才能驱动发声。详情参开以下文章。 八、51单片机之蜂鸣器_51蜂鸣器_朱嘉…

DINO代码学习笔记(三)

DINO代码学习笔记(一)中已经将输入transformer之前的参数处理给捋了一遍 DINO代码学习笔记(二)中将encoder部分给捋了一遍 本篇进入decoder,这里先对encoder做一些假设,基于DINO代码学习笔记(…

使用Harbor 和 Kraken 优化镜像拉取速

一、P2P镜像分发简述 随着云原生架构被越来越多的企业接受,企业应用中容器集群的规模也越来越大。当容器集群达到一定的规模且单容器应用副本数达到一定级别时,集群中容器镜像的分发将面临挑战。   P2P(Peer-to-Peer,点对点&am…

股票策略社群实盘展示

量化策略开发,高质量社群,交易思路分享等相关内容 大家好,我是Le Chiffre 从今年1月份开始,我们开始了松鼠股票策略社群,历经5个月,发布了5个策略。其中有2个多因子,2个etf,1个网格…

一款企业级的供应链采购系统,已开源

介绍 基于pig微服务架构打造 供应链系统,采购配送系统。为客户提供仓储管理、订单管理、打单、货源采购、分拣、配送等系统功能。 软件架构 采用 J2EE 技术体系,基于Spring Cloud微服务框架进行封装,平台设计灵活可扩展、可移植、可应对高…

Java使用EasyCode自动生成代码工具分享

此为内容创作模板,在发布之前请将不必要的内容删除 做Java的项目开发,会使用各种代码自动生成工具,今天就来介绍一款EasyCode插件,废话不多说了,看看怎么安装和使用。 一、EasyCode插件 EasyCode的安装 在Idea的Sett…