今天讲讲MYSQL数据库事务怎么实现的!

news2024/11/30 8:45:06

目录

什么是数据库事务

Mysql如何保证原子性

Mysql如何保证持久性

MySQL怎么保证隔离性

事务隔离级别

脏读的解决

不可重复读的解决

幻读的解决

MVCC实现

Read View

那么RC、RR级别下的InnoDB快照读有什么不同?


什么是数据库事务

数据库事务是指一组数据库操作,这些操作必须被视为一个不可分割的单元,要么全部执行成功,要么全部失败回滚。事务通常由多个SQL语句组成,这些语句可以读取、插入、更新或删除数据库中的数据。
事务具有ACID属性:

  1. 1. 原子性(Atomicity):事务的所有操作被视为单个原子操作,要么全部执行成功,要么全部执行失败回滚。

  2. 2. 一致性(Consistency):事务执行的结果必须使数据库从一个一致性状态转换到另一个一致性状态,其中包括所有数据完整性和约束性规则的应用。

  3. 3. 隔离性(Isolation):一个事务的执行不能被其他并发执行的事务干扰,每个事务应该感觉自己在独立地执行。

  4. 4. 持久性(Durability):一旦事务提交,其结果应该持久保存在数据库中,即使系统故障也应该如此。

通过实现事务,数据库系统可以确保数据的完整性和一致性,以及并发访问时的正确性。如果一个事务中的任何一个操作失败,整个事务将被回滚到最初的状态,这确保了数据库的一致性。

Mysql如何保证原子性

undo log名为回滚日志,是实现原子性的关键。InnoDB把这些为了回滚而记录的这些东西称之为undo log。这里需要注意的一点是,由于查询操作(SELECT)并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的undo logundo log主要分为3种:

  • • Insert undo log :插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。

  • • Update undo log:修改一条记录时,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。

  • • Delete undo log:删除一条记录时,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。

    • • 删除操作都只是设置一下老记录的DELETED_BIT,并不真正将过时的记录删除。

    • • 为了节省磁盘空间,InnoDB有专门的purge线程来清理DELETED_BIT为true的记录。为了不影响MVCC的正常工作,purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view);如果某个记录的DELETED_BIT为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。

举个栗子:

sqlundo log
insertdelete
deleteinsert
update T set v=3 where v=1update T set v=1 where v=3

Mysql如何保证持久性

通过Innodb架构解析我们了解到InnoDB 为了提升读写效率,引入了Buffer Pool(缓存池):

  • • 当数据库读取数据时,会首先从缓存池中读取

  • • 往数据库写入数据时,会先写入缓存池

  • • 缓存池中更新的数据会定期刷新到磁盘中

如果MySQL宕机,缓存池中更新的数据还没有刷回到磁盘中,就会导致数据丢失。于是,redo log被引入进来解决这个问题。

图片

  1. 1. 先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝。

  2. 2. 生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值。

  3. 3. 当事务commit时,将redo log buffer中的内容刷新到 redolog file,对 redo log file采用追加写的方式。

  4. 4. 定期将内存中修改的数据刷新到磁盘中。

redoundo在一次事务操作中是如何交互的?假设有A、B两个数据,值分别为1、2,开启事务分别对其进行修改A → 3,B → 4,在提交,过程如下:

事务redo&undo logo
begin;开启事务
记录A->3到redo log buffer
update T set A=3 where A=1;A修改为3
记录A=1到undo log
记录B->4到redo log buffer
update T set B=4 where B=2;B修改为4
记录B=2到undo log
记录A->3到redo log记录B->4到redo log
commit;事务提交

MySQL怎么保证隔离性

事务在并发情形下会互相干扰到的操作大体可以分为两类,与之相对应地,MySQL采用了两种方式来实现它们的隔离:

  1. 1. 一个事务的写操作对另一个事务的写操作的影响:锁机制保证隔离性

  2. 2. 一个事务的写操作对另一个事务的读操作的影响:MVCC保证隔离性

加锁:读取数据之前,对其加锁,阻止其他事务对数据进行修改

MVCC:不加任何锁,采用多版本并发控制实现,把数据库的行锁和行的多个版本结合起来,可以实现非锁定读,从而提高数据库的并发性能。

事务隔离级别

当数据库上有多个事务同时执行的时候,会带来以下问题:

问题描述举例
脏读一个事务读到了另一个事务未提交修改的数据。事务A开始一个更新操作,但是还没有提交,这时事务B读取了这个未提交的数据,就会产生脏读。
幻读一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据。事务A进行一个范围查询,此时事务B插入了一些符合该范围查询条件的新数据,当事务A再次进行相同的范围查询时,会发现多了一些之前没有的行,就产生了幻读。
不可重复读在一个事务中,多次查询的数据不一致。事务A读取了一行数据,然后事务B对这一行数据进行了更新,并且提交了,当事务A再次读取这一行数据时,会发现数据已经发生了变化,就产生了不可重复读。

为了避免这些问题的出现,数据库引入了隔离级别的概念,通过对不同隔离级别的设置,可以控制事务之间的隔离程度,从而避免并发问题的产生。不同的隔离级别有不同的特点和使用场景,需要根据实际情况进行选择。

以下是四个标准的事务隔离级别:

隔离级别含义脏读不可重复读幻读
读未提交,Read Uncommitted事务中的修改,即使没有提交,对其他事务都是可见的YYY
读已提交,Read Committed事务从开始到提交之前,所做的修改对其他事务都不可见NYY
可重复读,Repeatable read同一事务中多次读取同样的记录结果是一致的NNY
可序列化,Serializable在读取的每一行数据上加锁,强制事务串行执行NNN
脏读的解决

Innodb是通过在每行数据中增加一个隐藏的事务ID来实现mvcc,当一个事物开始时他会获取一个唯一的事务ID,该事务ID用来标记事务做的修改。当事务读取一行数据时,innodb会检查该行数据事务ID是否小于当前事务ID,如果是说明该行数据是未提交的数据,innodb会阻止该事务读取该行数据,从而避免了脏读的问题。

不可重复读的解决

innodb通过mvcc解决不可重复读的问题,在RR数据库隔离级别下,当我们使用快照进行数据读取的时候,只会在第一次读取的时候生成一个ReadView,后续所有快照读都是使用同一个快照,所以就不会发生不可重复读的问题了。

可重复读模式下举个栗子:事务隔离级别为RR:

图片

创建个测试表,并插入一条数据(1,1,1)

create table table1(
    id int(11) not null,
    a varchar(50) default null,
    b varchar(50) default null,
    primary key(id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

分别开启两个事务测试:

事务1事务2
启动事务,查询如下:

图片

启动事务,查询如下:

图片

~将a改为2,可以查到

图片

查询a的结果还是1

图片

~提交事务

图片

再次查询a的结果还是1

图片

提交事务,再次查询a的结果变为2了

图片

幻读的解决

innodb的mvcc和间隙锁在一定程度上避免了幻读的发生,但是没有办法完全避免,当一个事务读的时候会导致幻读的发生。

幻读的case

  • • 创建一个用户表

    create table user(
        id int not null,
        name varchar(50),
        age int,
        primary key(id)
    );
  • • 插入几条数据

    insert into user values(1,'张三',10),(2,'李四',20),(3,'王二',30);

    图片

  • • 分别开启两个事务测试:

事务1事务2
begin;select * from user where age >10 and age<40;

图片

begin;insert into user value(4,'麻子',25);

图片

commit;
select * from user where age >10 and age<40;

图片

update user set name='呵呵' where age=25;select * from user where age >10 and age<40;

图片

MVCC实现

每条记录在更新的时候都会同时记录一条回滚操作。同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。

MySQL中每条记录,除了我们自定义的字段之外,还有数据库隐藏定义的三个字段:

字段描述
DB_TRX_ID6字节,最近修改事务id,记录创建这套记录后者最后一次修改该记录的事务id
DB_ROLL_PTR7字节,回滚指针,指向这条记录的上一个版本,用于配合undolog
DB_ROW_ID6字节,隐藏的主键,如果数据表没有主键,那么innodb会生成一个6字节的row_id

在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

InnoDB 并不会真正地去开辟空间存储多个版本的行记录,只是借助 undo log 记录每次写操作的反向操作。所以B+ 索引树上对应的记录只会有一个最新版本,InnoDB 可以根据 undo log 得到数据的历史版本,从而实现多版本控制。

Read View

什么是Read View,说白了Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)

所以我们知道 Read View主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个Read View读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,即可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。

Read View遵循一个可见性算法,主要是将要被修改的数据的最新记录中的DB_TRX_ID(即当前事务ID)取出来,与系统当前其他活跃事务的ID去对比(由Read View维护),如果DB_TRX_ID跟Read View的属性做了某些比较,不符合可见性,那就通过DB_ROLL_PTR回滚指针去取出Undo Log中的DB_TRX_ID再比较,即遍历链表的DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的DB_TRX_ID, 那么这个DB_TRX_ID所在的旧记录就是当前事务能看见的最新老版本

假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。

图片

当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。

那么RC、RR级别下的InnoDB快照读有什么不同?

在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;

在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。

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

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

相关文章

2024年4月12日 十二生肖 今日运势

小运播报&#xff1a;2024年4月12日&#xff0c;星期五&#xff0c;农历三月初四 &#xff08;甲辰年戊辰月丙午日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;羊、狗、虎 需要注意&#xff1a;牛、马、鼠 喜神方位&#xff1a;西南方 财神方位&#xff1a;…

强化学习-深度蒙特卡洛算法(Deep Monte-Carlo)解决骰子游戏“吹牛”

一、算法简介 深度蒙特卡洛算法是一种使用深度神经网络来进行蒙特卡洛估计的强化学习算法&#xff0c;它最早于2020年在《DouZero: Mastering DouDizhu with Self-Play Deep Reinforcement Learning》被提出用于解决斗地主问题。 深度蒙特卡洛算法使用深度网络拟合每个时刻&…

HarmonyOS 开发-阻塞事件冒泡

介绍 本示例主要介绍在点击事件中&#xff0c;子组件enabled属性设置为false的时候&#xff0c;如何解决点击子组件模块区域会触发父组件的点击事件问题&#xff1b;以及触摸事件中当子组件触发触摸事件的时候&#xff0c;父组件如果设置触摸事件的话&#xff0c;如何解决父组…

应对人工智能在金融服务业的迅猛发展

今天分享的是人工智能专题系列深度研究报告&#xff1a;《人工智能专题&#xff1a;应对人工智能在金融服务业的迅猛发展》。 &#xff08;报告出品方&#xff1a;安永&#xff09; 评估人工智能对金融服务业的潜在影响 虽然大型银行和保险公司使用人工智能已有多年&#xf…

Rust语言入门第一篇-环境搭建

Rust语言入门第一篇 Rust官网 一&#xff0c;环境搭建 1、C开发环境配置 Rust 语言的底层是依赖于 C/C 编译器的。在安装 Rust 编译器时&#xff0c;通常会自动安装所需的 C/C 编译环境&#xff0c;以便 Rust 能够生成可执行文件或库。因此&#xff0c;在安装 Rust 之前&…

牛客NC413 两个升序数组的中位数【hard 数组,模拟 Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/b3b59248e61f499482eaba636305474b 思路 直接模拟2个数组有顺序放到一个数组中help中如果help长度为奇数&#xff0c;返回中间的数如果help长度为偶数&#xff0c;返回中间2个数的和除以2参考答案java import j…

【学习】移动端兼容性测试有什么方法及重要性

随着移动互联网的快速发展&#xff0c;移动应用程序已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;由于各种移动设备的硬件和软件差异&#xff0c;移动应用程序的兼容性问题也越来越突出。因此&#xff0c;移动端兼容性测试成为了一个重要的环节&#xff0c;它可以…

jar包瘦身 移除无用的maven依赖

1.进入pom.xml所在目录&#xff0c;执行mvn dependency:analyze Unused declared dependencies found:下的单个包进行逐个移除编译&#xff0c;编译不报错就可以移除

3d怎么按路径制作模型---模大狮模型网

在3D建模中&#xff0c;按路径制作模型是一种常见的技术&#xff0c;特别适用于创建曲线、管道、绳索等线性形状的物体。虽然这项技术可能对初学者来说有些复杂&#xff0c;但通过一步步的指导和实践&#xff0c;你将能够掌握它。本文将详细介绍按路径制作模型的步骤&#xff0…

4月9号总结

java学习 一.steam流 1.介绍 Stream 是 Java 8 中引入的一种处理集合数据的新抽象。它提供了一种高效且便利的方式来处理集合中的元素&#xff0c;支持函数式编程的特性&#xff0c;使得集合操作变得更加简洁和灵活。 2.创建 List和Set可以直接调用接口的steam方法转换为流 …

vue2+codemirror实现指定行背景染色(二 代码染色)

在做覆盖率统计时,我们需要给指定代码行染色高亮,这里使用vue2+codemirror实现代码的渲染和染色。效果图如下: 1、安装 vue-codemirror // 指定安装4.x版本 // 目前最新版本6.x,仅支持Vue3.0 npm i vue-codemirror@4.x --save// codemirror 需要与 vue-codemirror 同时安装…

JavaScript_语法--变量

1.4 变量 变量&#xff1a;一小块存储数据的内存空间 Java语言是强类型语言&#xff0c;而JavaScript是弱类型的语言 强类型&#xff1a; 在开辟变量存储空间时&#xff0c;定义了空间将来存储的数据的数据类型。只能存储固定类型的数据 弱类型&#xff1a; 在开辟变量存储空间…

Ollama教程——兼容OpenAI API:高效利用兼容OpenAI的API进行AI项目开发

相关文章: Ollama教程——入门&#xff1a;开启本地大型语言模型开发之旅 Ollama教程——模型&#xff1a;如何将模型高效导入到ollama框架 Ollama教程——兼容OpenAI API&#xff1a;高效利用兼容OpenAI的API进行AI项目开发 Ollama教程——兼容OpenAI API&#xff1a;高效利用…

我为什么选择成为程序员?

前言&#xff1a; 我选择成为程序员不是兴趣所在&#xff0c;也不是为了职业发展&#xff0c;全是生活所迫&#xff01; 第一章&#xff1a;那年&#xff0c;我双手插兜&#xff0c;对外面的世界一无所知 时间回到2009年&#xff0c;时间过得真快啊&#xff0c;一下就是15年前…

基于小程序实现的校园失物招领系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

加固系统安全,防范ssh暴力破解之Fail2Ban

你是否还在担心你的服务器被攻击&#xff1f;你是否还在担心你的博客的安全&#xff1f;你是否还在担心你的隐私&#xff1f;别急fail2ban它来了&#xff0c;它可以解决你的一切问题。 Fail2Ban 是什么&#xff1f; 现在让我们一起来认识一下今天的主角 – Fail2Ban。简单说来…

多级菜单Mysql数据库表设计与创建

1.还是以Vue实现学院官网为例 文章地址&#xff1a;http://t.csdnimg.cn/jrJhE Vue 实现学院官网“菜单”当时是使用静态数据&#xff0c;也就是在页面上写死了的。 今天我们需要将“菜单”数据在数据库中进行维护&#xff0c;我们使用的是Mysql数据库 2.数据库的设计 我们的…

2023年通用人工智能AGI等级保护白皮书

今天分享的是人工智能专题系列深度研究报告&#xff1a;《人工智能专题&#xff1a;2023年通用人工智能AGI等级保护白皮书》。 通用人工智能发展现状 本章主要介绍通用人工智能的基本情况&#xff0c;包括其发展历史、现状以及组成架构等内容。本文还将通过从技术角度出发来分…

btSoftRigidDynamicsWorld 类是 Ammo.js 物理库中的一个类,表示一个动态世界,用于处理软体和刚体物体的物理模拟。

demo案例 btSoftRigidDynamicsWorld 类是 Ammo.js 物理库中的一个类&#xff0c;表示一个动态世界&#xff0c;用于处理软体和刚体物体的物理模拟。让我们按照输入参数、输出、属性和方法来详细解释其 API&#xff1a; 输入参数&#xff1a; dispatcher&#xff1a;这是一个…

多线程的学习

多线程编辑&#xff1a; 可以简单理解进程是一个软件 而线程就是一个软件中多个可以同时运行的功能 实现多线程的第一种方式&#xff1a;使用Thead类我们再自己创造一个类继承于这个类我们在对Thead方法进行重写&#xff0c;注意我们再重写的时候一定要加上Override这行 我犯下…