MySQL是如何保证数据一致性的?

文章目录

  • 前言
  • MySQL保证的一致性
  • MySQL发生不一致环节
    • 并发冲突
    • redolog不完整
    • binlog&redolog不一致
  • MySQL解决不一致方案
    • 加锁解决并发冲突
    • undolog解决redolog不完整
    • XA两阶段提交解决binlog和redolog的不一致
  • 总结

前言

通过上文《MySQL是如何保证数据不丢失的?》可以了解DML的操作流程以及数据的持久化机制。对于一个数据库而言,除了数据的持久性、不丢失之外,一致性也是非常重要的,不然这个数据是没有任何意义的。在使用MySQL时,数据不一致的情况也可能出现,所以,本文就来看看MySQL是如何保证数据一致的。

MySQL保证的一致性

在这之前先划清一下界限,看一下MySQL保证的是哪里的一致性。

拿一个最简单的转账例子,用户A向用户B转1000元,正常的sql是这样的

update account set balance=balance-4000 where user='A' and balance >= 4000;
update account set balance=balance+4000 where user='B';

示例表数据如下
在这里插入图片描述
如果最终用户A账户没有扣4000,而用户B账户多了4000,总金额也无缘无故的多了4000。这个时候就造成了数据不一致了。出现这个问题可能存在几个原因:

  1. 在MySQL客户端执行sql时没有做校验。如果用户A余额并不足以4000,由于没有校验,两条sql都会成功执行,用户B就会凭空多出4000。
  2. 两条sql并不在同一事务中。可能sql1执行失败了,sql2执行成功,由于不再一个事务中导致用户B多了4000。
  3. 在MySQL内部执行时因为某些故障而出现了不一致情况。

很显然,第三点是需要MySQL解决处理的。而第一点是属于MySQL客户端的逻辑BUG,第二点会存在客户端在使用事务时不遵循规则的情况,都属于外部因素,MySQL不可控

所以,MySQL保证的一致性是:在一个事务中的DML(增删改)操作。尽管DML本身可能存在问题。

MySQL发生不一致环节

划清界限后再分析一下在DML执行过程中,哪个环节会发生数据不一致。 以上面的sql为例,假设已经进行过校验且在同一事务。

并发冲突

在执行第一条sql时,「执行器」会通过条件user='A' and balance >= 4000在「存储引擎」获取到符合条件的记录,然后进行balance扣减操作。(不知道这个流程的可以看下前面的文章)

如果这个时候存在并发现象,扣减操作可能会执行多次,这个balance肯定就不是预想中的结果了,也就发生数据不一致了。如下图

在这里插入图片描述

当3个update请求同一时间调用存储引擎对同一数据页更新后,正常情况下,balance值应该为0。但是因为并发操作,balance的值可能会被修改为-1000或者-2000等其他值,这样的bug显然是不可被接受的。

有并发经验的应该都知道需要通过锁资源可以避免这个情况,InnoDB也是通过加来处理的。

redolog不完整

通过上文可以知道,InnoDB是通过「双写缓冲」、「Redo Log」等机制保证数据不丢失的。

这种情况下,假设第一条sql执行成功并且对应的redo log已经被刷新到磁盘中,但是第二条sql执行失败或者MySQL服务宕机导致其redolog未刷新到磁盘,那么在下次启动恢复时,就会发生数据不一致了。如下图

在这里插入图片描述

sql示例的第一条执行结果通过redolog恢复了,但是第二条的redolog随着宕机丢失了,于是乎造成了数据的不一致。(redo log的刷盘机制和构建脏页可以通过上文进行了解。)

对于这种情况,InnoDB是通过上文提到的「Undo Log」来解决的。

binlog&redolog不一致

我们知道,binlog中记录了所有对数据更新的原始sql,以便数据备份恢复、主从复制。与redolog不一样的是binlog属于MySQL server层,而redolog是InnoDB的机制,用于故障恢复,两者并不冲突,这里不过多赘述。

虽然不冲突,但是要保证两者在事务提交后都可以持久化到磁盘,不然就会在主从复制的时候出现数据不一致现象,如下图

在这里插入图片描述

只要binlog和redolog有一方没有同步持久到磁盘都会发生类似现象。针对这种情况MySQL是通过两阶段提交解决的。

以上就是DML在执行过程中可能出现不一致的环节(没有想到的欢迎评论交流)。接下来具体看一下InnoDB针对以上几种情况是如何处理解决,从而保证数据一致性的。

MySQL解决不一致方案

加锁解决并发冲突

锁没有什么好说的,innoDB根据隔离级别决定是否用锁(当然,还有server层的表锁什么的这里不展开)。这里就演示下在隔离级别REPEATABLE-READ下,锁在SQL执行中的具体作用和效果。

当在第一个事务中执行 update account set balance=balance-4000 where user='A' and balance >= 4000; 时,其他事务不能对user为’A’的记录进行更新。如下图,当第二个事务窗口执行 update account set balance=balance-1000 where user='A' and balance >= 1000; 时会被阻塞住,直到第一个事务提交或者超时。

在这里插入图片描述

这个时候可以通过 select * from sys.innodb_lock_waits ; 查看一下锁的相关信息

在这里插入图片描述

这里的locked_type是RECORD,也就是行记录锁,还有一个是间隙锁。

间隙锁的作用是保证某个范围内的数据在锁定情况下不会发生任何变化。比如,当第一个事务执行update account set balance=balance-100 where id between 7 and 9;后,第二个事务在执行INSERT INTO account (id, user,balance) VALUES (8, 'ABD',5000);时会阻塞,但是执行 INSERT INTO account (id, user,balance) VALUES (16, 'ABDD',5000);会成功执行,因为插入id为16的行数据不会影响到7~9之间的数据。这个时候去查看select * from sys.innodb_lock_waits;时会发现waiting_lock_mode值为 X,GAP(间隙)

在这里插入图片描述

所以说,锁避免了事务的并发访问导致的数据不一致。

undolog解决redolog不完整

InnoDB在因sql执行失败或者MySQL服务宕机导致redolog不完整从而出现数据不一致是这么解决的:

  1. 在更新数据页之前,InnoDB会先将数据当前的状态记录在「Undo Log」中。
  2. 之后,再将更新后的相关数据记录到「Redo Log」中。

这样的话,不论出现哪种情况都可以通过undo log将数据回滚并保持一致,这个就是经常提到的原子性以及「回滚」操作。

就如上图(redo log不完整环节),加上Undo log之后数据状态如下图

在这里插入图片描述

图中加了行记录的隐藏字段事务ID和回滚指针以及undo log页和undo的redo。

undo log 记录的就是user='A’和‘B’事务提交前的数据,各为4000。

redo log 中会记录所有的更新操作,包括undo,因为undo记录的也是更新语句。需要说一下,这里记录的undo是演示使用,对于一条update操作,真正的undo会记录一条delete和一条insert操作,原因上文有介绍。

为什么redo log会记录undo

undo log是以页为单位,跟随页的刷新机制,会存在丢失的情况,所以在记录undo后也会将该undo记录到redo,避免undo丢失,一旦undo丢失就回滚不了了。

有了undo log后,假设第二条sql执行失败,这个时候就会通过行记录中的事务ID(txidx)和回滚指针(roll_pointx、roll_pointx1)去undolog中找对应的回滚操作(如图中的 ‘**回滚指针’**箭头),最终将事务回滚保证原子性和一致性。

针对上图的状态,如果发生宕机,那么在重新MySQL服务时,会有两个操作:

  1. 会先通过redo log构建「脏页」。
  2. 根据redo log中记录的事务提交状态来决定是否回滚。

如图

在这里插入图片描述

当前user='A’的事务状态为prepare,所以需要进行回滚操作。回滚流程是这样的:

  1. 根据数据中该记录的事务ID(txidx)在undolog中找对应的回滚操作。
  2. 发现事务ID有两个undo操作,user='A’和‘B’的。
  3. 执行undo操作,将数据页中的记录回滚至事务提交前状态。

最终的结果就是user='A’和‘B’的balance会回滚到4000。

所以说,undo避免了事务或者宕机的异常导致的数据不一致。

XA两阶段提交解决binlog和redolog的不一致

redo log中的事务状态不仅在这里起到作用,在binlog和redolog的一致上,同样是通过这个状态来判断并且决定是否需要回滚。

这个就不得不说到MySQL的XA两阶段提交协议了,在这之前,我一直以为XA是运用到MySQL与外部应用的,没想到是应用在MySQL内部的。不过分布式事务嘛,原理基本上都一样,想要深入了解的可以看《分布式事务及解决方案》,这里就不过多赘述了。

XA的两阶段分别是prepare和commit,在事务提交前,redolog中记录的状态都是prepare,当事务提交后,该状态就会被更新为commit,同时将XID写入到对应的binlog中并刷新到磁盘。如下图

在这里插入图片描述

这样的话,如果发生宕机,下次启动时可以根据redolog中的状态以及XID去binlog中查找,如果存在意味着两者一致,不存在就进行回滚操作。

所以说,XA两阶段提交保证了binlog和redolog逻辑一致,从而避免主从节点的数据不一致。

总结

MySQL一致性的保证基本上涉及到InnoDB存储引擎的各个组件,「Buffer Pool」、「Log Buffer」、「Redo Log」、「Undo Log」等,还有DML操作的流程、锁、故障恢复等功能。最后再总结下MySQL是如何保证一致性的。

  1. 对于并发操作带来的数据不一致性问题,InnoDB通过锁来解决。
  2. 对于可能会发生的redolog不完整的情况,InnoDB通过Undo Log来解决。
  3. 对于redolog&binlog不一致带来的主从节点数据不一致,MySQL是通过XA两阶段提交来解决。

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

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

相关文章

Ubuntu安装CUDA出在三个cuda相关文件夹?

按照网上的教程,在/usr/local中操作cuda文件夹,但是发现这里会出现不止一个cuda文件夹: 可以看大这里有cuda、cuda-11、cuda-11.8三个文件夹,实际上我安装的是11.8的cuda,那么第三个文件是好理解的,就是我…

Django Web框架

1、创建PyCharm项目 2、安装框架 pip install django4.2.0 3、查看安装的包列表 4、使用命令创建django项目 django-admin startproject web 5、目录结构 6、运行 cd web python manage.py runserver7、初始化后台登录的用户名密码 执行数据库迁移生成数据表 python man…

Mybatis-plus动态表名配置

一、pom文件依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency> 二 、mybatis配置类 2.1 表名设置工具类 TableNameHelper pack…

React-hook-form-mui(二):表单数据处理

前言 在上一篇文章中&#xff0c;我们介绍了react-hook-form-mui的基础用法。本文将着表单数据处理。 react-hook-form-mui提供了丰富的表单数据处理功能&#xff0c;可以通过watch属性来获取表单数据。 Demo 下面是一个使用watch属性的例子&#xff1a; import React from…

【Redis交响乐】Redis中的数据类型/内部编码/单线程模型

文章目录 一. Redis中的数据类型和内部编码二. Redis的单线程模型面试题: redis是单线程模型,为什么效率之高,速度之快呢? 在上一篇博客中我们讲述了Redis中的通用命令,本篇博客中我们将围绕每个数据结构来介绍相关命令. 一. Redis中的数据类型和内部编码 type命令实际返回的…

【MATLAB】EMD_LSTM神经网络时序预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 EMD-LSTM神经网络时序预测算法是一种结合了经验模态分解&#xff08;EMD&#xff09;和长短期记忆神经网络&#xff08;LSTM&#xff09;的时间序列预测方法。 EMD是一种处理非平稳信号的…

Linux的引导过程与服务控制

一.开机启动的完整过程 引导过程&#xff1a; 1.bios加电自检 检测硬件是否正常&#xff0c;然后根据bios中的启动项设置&#xff0c;去找内核文件 服务器主机开机以后&#xff0c;将根据主板BIOS中的设置对CPU、内存、显卡、键盘灯设备进行初步检测&#xff0c;检测成功后根…

stable diffusion 基础教程-图生图

界面 图生图大概有以下几个功能: 图生图涂鸦绘制局部绘制局部绘制(涂鸦蒙版)其常用的也就上面四个,接下来逐步讲解。 以图反推提示词 图生图可以根据反推提示词来获取相应图片的提示词,目前3种主流方式,如下: CLIP反推提示词:推导出的文本倾向于自然语言的描述方式,…

openmediavault(OMV) (26)网络(1)ddns-go

简介 "ddns-go" 是一个动态域名解析(Dynamic DNS)工具,用于更新域名的IP地址。它可以自动检测你的公共IP地址,并将其更新到指定的域名解析服务商,以确保你的域名始终与最新的IP地址相匹配。 安装 hub.docker.com上下载ddns-go镜像 配置compose文件 --- versio…

C++系列十一:C++指针

C指针 1. 指针的声明和初始化2. 指针的运算3. 指针与数组4. 指针与函数参数传递5. 指针与动态内存分配6. 指针与多维数组7. 指针与函数返回值8. 指针与内存管理9. 指针的高级应用 指针是C中一个非常重要的概念&#xff0c;它是指向变量、数组或对象的内存地址的引用。通过指针&…

LeetCode 466. 统计重复个数,循环字符串匹配优化

一、题目 1、题目描述 定义 str [s, n] 表示 str 由 n 个字符串 s 连接构成。 例如&#xff0c;str ["abc", 3] "abcabcabc" 。 如果可以从 s2 中删除某些字符使其变为 s1&#xff0c;则称字符串 s1 可以从字符串 s2 获得。 例如&#xff0c;根据定义&a…

在 sealos 上使用 redisinsight 完美管理 redis

先起一个 redis 集群&#xff0c;在 sealos 上可以点点鼠标就搞定&#xff1a; 简单两步&#xff0c;redis 集群搞定。 再启动 RedisInsight, 是一个 redis 的可视化管理工具。 就可以看到部署后的地址了。进去之后填写 redis 的链接信息即可&#xff1a; 链接信息在数据库的…

STM32MP157/linux驱动学习记录

1. uboot烧录 2.linux安装nfs服务 sudo apt-get install nfs-kernel-server rpcbind安装nfs服务 在用户根目录下创建一个名为“linux”的文件夹&#xff0c;以后所有的东西都放到这个“linux”文件夹里面&#xff0c;在“linux”文件夹里面新建一个名为“nfs”的文件夹&#…

【Rust日报】2024-01-01 使用 Rust 构建生产级微服务

使用 Rust 构建生产级微服务 这个由sanyi编写的博客系列 “在Rust中构建生产就绪微服务” 涵盖了Rust中构建生产就绪微服务的过程。以下是每篇博客的主要主题总结&#xff1a; 设置工作空间&#xff1a; 为Rust应用程序设置多包工作空间。 添加CLI子命令&#xff1a; 实现一种简…

Android 车联网——CarPropertyService介绍(三)

一、简介 Android CarPropertyService 是一个用于与车辆系统通信的服务。它是 Android Auto 中的一个重要组件,绝大部分与车辆硬件功能相关联的属性,如空调、座舱功能、车辆传感器等都是通过 CarPropertyService 来读取或者设置的。 CarPropertyManager 是 CarPropertyServic…

代码训练营Day.22 | 235. 二叉搜索树的最近公共祖先、701. 二叉搜索树中的插入操作、450. 删除二叉搜索树中的节点

235. 二叉搜索树的最近公共祖先 1. LeetCode链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 2. 题目描述 3. 解法 利用二叉搜索树的特性进行公共节点的判断&#xff1a; 1. 此节点为公共节点&#xff1a;p、q恰好在此节点的左右棵子树上。即…

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-8Lag Compensator滞后补偿器

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-8Lag Compensator滞后补偿器 从稳态误差入手&#xff08;steady state Error&#xff09; 误差 Error &#xff1a; E ( s ) R ( s ) − X ( s ) R ( s ) − E ( s ) ⋅ K G …

Android 车联网——CarManager管理器续(五)

上篇文章介绍了中的车辆信息服务、车辆传感服务、车内空调系统服务,这一篇我们继续分析剩下的座舱服务、制造商扩展服务。 一、管理器介绍 1、座舱服务 源码位置:/packages/services/Car/car-lib/src/android/car/hardware/cabin/CarCabinManager.java CarCabinManager 提…

Open3D 最小二乘拟合平面——拉格朗日乘子法

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接Open3D 最小二乘拟合平面——拉格朗日乘子法。爬虫自重。 一、算法原理 设拟合出的平面方程为: a x + b y +

c# 编程点滴--元组

1. 元组 Tuple 是 C# 中表示元组&#xff08;Tuple&#xff09;的数据结构。元组是一个用于存储一组有序元素的数据结构&#xff0c;每个元素可以是不同类型的数据。在 C# 中&#xff0c;元组是值类型&#xff0c;允许存储多个值&#xff0c;并且可以通过索引或者具名字段访问…