MySQL-事务

news2024/11/26 4:20:48

文章目录

      • 事务(Transaction)
        • 为什么会出现事务
          • ACID四大属性
          • 事务提交的方式
          • 事务基本操作:
        • 事务隔离级别(MVCC+)
          • 隔离级别:
          • 如何理解隔离性?
          • 为何要存在隔离级别?
          • 一致性
            • 读读并发
            • 写写并发
            • 读写并发
            • 三个隐藏列字段
        • undo log
          • 模拟MVCC版本链
          • 总结
        • ReadView
          • 实验
          • RR和RC本质区别
            • 例一:实现效果是RR级别
            • 例2:实验效果是RC级别

事务(Transaction)

MySQL一定会遇到并发问题,内部都自己解决了,采用的策略就是事务。因为并发问题一张票卖给两个人了就是。

在用户层的角度:你要完成什么样的业务叫做事务。

在MySQL角度:为了完成一个目的的步骤中每一个sql操作(上层理解是具有逻辑关系的)整合在一起就叫事务。

  • 例如,我给别人转账,先把我的账户余额减去,再把别人的加上去,这两部合在一起叫事务。

所有的sql操作一半都会被MySQL包装成为事务,以事务的方式提交。

为什么会出现事务

本质是为了当应用程序访问数据库的时候,事务能够自己解决潜在错误和并发问题。原子性要么提交要么回滚,就不用考虑网络异常等情况。因此事务本质上是为了应用层服务的,而不是伴随数据库系统天生就有的。

InNoDB支持事务,myisam不支持事务。

ACID四大属性
  • 原子性A:一个事务的所有的操作要么全部完成,要么全部不完成。如果发生错误,就会回滚到事务开始前的状态。
  • 一致性C:
  • 隔离性I:互相不要影响
  • 持久性D:事务处理之后对数据的修改是永久的即便系统故障也不回丢失。
事务提交的方式

手动提交和自动提交,autocommit=1就是自动提交

show variables like 'autocommit';查看事务提交方式

set autocommit =1;设置为自动提交,=0禁止自动提交

事务基本操作:
set global transaction isolation level read uncommited;--设置默认隔离级别是读未提交
select @@tx_isolation;--查看隔离级别
start transaction;--命令行启动事务
begin;
savepoint s1;--设置回滚点
rollbackto s1;--回滚到s1这个点
rollback;--回滚到起始点,将操作都撤销

非正常操作(读未提交隔离级别):

  • 实验一:未提交commit直接ctrl+\中止了Aborted,客户端崩溃,数据直接回滚恢复到最开始。–原子性

手动启动(start transaction/begin )一个事物的时候和MySQL中是否事务会自动提交无关。

  • 实验二:commit提交之后已经被提交到数据库之后就不能再被回滚了–持久性

  • begin操作会自动更改提交方式,就叫手动提交,和系统默认的自动提交就无关了。

    • 自动提交设置是给谁设置的呢?会影响谁呢?

      其实我们之前的所有单条sql本质在MySQL中全部各自会被以事务的方式进行提交的,自动提交的,不需要begin什么的。select查询并不受影响,当我们insert数据之后,一旦ctrl+\异常中止,数据就会回滚,插入的单条语句的数据也会丢失。

    在这里插入图片描述

    自动提交就是给mysql中单条SQL设置的,我们的默认行为

    • 一个事务已经被commit之后就没办法回滚了。

事务隔离级别(MVCC+)

如何理解隔离性?

数据库中,为了保证事务执行过程中尽量不受干扰,就有了一个特征叫隔离性。

数据库中,允许事务收到不同程度的干扰,就有了隔离级别。

隔离级别:
  • 读未提交(Read Uncommited):
select @@tx_isolation;--查看事务隔离属性
select @@gloabl.tx_isolation;--全局隔离级别
select @@session.tx_isolation;--只会影响本次客户端的隔离界别,再次登录就没了

读到了另一个事务还未提交的数据,对方可以回滚,错误的指导了另一个事务进行操作。相当于没有任何隔离性,也有很多的并发问题,脏读

如下图的我插入错误的唐僧然后回滚删除了,却提前被另一端的另一个事务读走了,可能就在上层为唐僧建立了等等数据结构。
在这里插入图片描述

不同的隔离级别是由锁的不同的使用决定的。

select @@global.tx_isolation;查看全局隔离级别。重启MySQL之后有效果对所有的回话都有影响。

select @@session.tx_isolation;查看本次会话的隔离级别。session隔离级别只会影响自己,并且立马有效。

set global transaction isolation level Repeatable Read;

  • 读提交(Read Commited)
set global transaction isolation level read commited;--读提交

读不到对端未提交的数据就是读提交了

问题:同一个事务内连续的读取出现了不一样的值(上帝视角是因为提交和没提交的时间点不同),造成不可重复读取,这是个问题吗?

  1. 你已经提交别人能读到,不应该等价于你已经提交,和你并行运行的事务也能读到。

  2. 不可重复读有什么问题?

    当一个作为逻辑判断的事务,读取表内容,判断结果受到修改内容的影响。表被其他三个同时运行的不同的事务(读提交)设置为三个不同的内容,导致逻辑判断事务连续做出三次判断结果,导致上层逻辑错误。

  • 可重复读(Repeatable Read)默认是RR级别

    set global transaction isolation level repeatable read;
    

    再怎么对数据进行修改尽管已经提交,我这个事务就是读不到修改甚至删除导致我读到的都是一样的结果。只有将我这个并行的事务结束掉,重新整一个就可以读到了,就是可重复读。

在这里插入图片描述

一般数据库,对插入操作无法进行屏蔽,尽管处于可重复读状态RR级别,会读到另一个事务新插入的数据,造成幻读问题。不过MySQL解决了。

  • 串行化

    对所有的操作全部加锁,进行串行化,只能一个个来,是事务之间的串行化。

set global transaction isolation level serializable;

即使是串行化,对于读操作是没有限制的。

两个并行读取的事务,一方事务想要修改就会卡主,只有另一个同时读取的事务发生commit之后,这一端才能够操作成功。

如何理解隔离性?

MySQL的内部机制让同时启动并发执行的各个事务,看到不同的数据修改(不包括读),就叫做隔离性。我们作为一个事务可看到不同可见性的数据,程度的不同叫做隔离级别。

为何要存在隔离级别?

隔离级别越严格,安全性越高,数据库的并发能力也就越低,只是为了安全,不需要种类繁多的隔离级别,无脑串行化就行。所以,安全+效率之间找平衡点,而这个平衡点不是MySQL决定的,采用什么方案什么隔离级别是由上层决定的,应用场景决定的。

一致性

事务执行的结果必须使得数据库从一个一致性状态变成另一个一致性状态。事务成功提交结果,数据库处于 一致性状态,事务执行中断而改未完成的事务对数据库所做的修改已经被写入数据库,此时数据库处于一种不一致额状态。

原子性持久性隔离性的目标就是为了维护一致性,一致性是由用户和MySQL共同决定的。是事务维护的最终目标。

读读并发

不存在任何问题

写写并发

有线程安全问题,存在更新丢失问题,串行化解决。

读写并发

多版本并发控制MVCC是一种解决读写冲突的无锁并发控制

每一个事务启动时都会有一个id,读写并发实际上只需要考虑访问同一条记录时读写操作并发的问题。

可通过事务id区分那一个事务先来的,所以肯定有先有后的再怎么并行。

三个隐藏列字段

DB_ROW_ID 6字节隐含的自增主键,innodb会自动产生一个聚簇索引。

DB_TRX_ID 6字节记录最近修改事务ID,创建这条记录修改该记录的事务ID。

DB_ROLL_PTR 7字节,回滚指针,指向这条记录的上一个被修改的版本。

flag:标识该记录是否有效,记录被更新或删除并不代表真的删除,而是删除flflag变了 。

undo log

mysql是以服务进程的方式在内存中运行,索引,事务,隔离性等都是在内存中完成的,即在mysql内部相关换乘区中保存相关数据完成判断操作,然后再合适的时候将相关数据刷新到磁盘中。简单理解为MySQL中的一段内存缓冲区,用来保存日志数据。

模拟MVCC版本链

首先创建一张表:student

mysql> create table if not exists student(
name varchar(11) not null,
age int not null
);
mysql> insert into student (name, age) values ('张三', 21);
Query OK, 1 row affected (0.05 sec)

虽然值创建了name 和age,但是还是会维护三个隐藏字段:

在这里插入图片描述

如果此时来了一个id=250的事务想要修改表中的内容,那么先写时拷贝将数据先暂存在undo log缓冲区,然后做相关字段的填充,依次类推形成一个历史版本链。

在这里插入图片描述

上面的一个一个版本,我们可以称之为一个一个的快照。 这样,我们就有了一个基于链表记录的历史版本链 。

回滚指针就是将这个版本连打上标签就行了。所谓的回滚,无非就是用历史数据,覆盖当前数据 。

总结

delete删除操作,不是将数据清空而是将flag标志设置为删除状态,也可以形成版本放到undo_log中。

如果是insert呢?因为insert是插入,也就是之前没有数据,那么insert也就没有历史版本。但是一般为了回滚操作,insert的数据也是要被放入undo log中,在他之前设置一个空版本,只要便于回滚就行。

如果当前事务commit了,那么这个undo log 的历史insert记录就可以被清空了。避免只进不出导致undolog内存临时缓冲区挤满。新插入说明以前没有你这个的历史版本,也没人用历史版本,所以你删除了历史版本留下最新版本就行了。

update修改delete删除的是可能存在历史版本的,你用别人也可能正在用,所以你不能删除历史版本,直到访问的事务都退出了才可以删除。

首先,select不会对数据做任何修改,所以,为select维护多版本,没有意义。不过,此时有个问题,就是:select读取,是读取最新的版本呢?还是读取历史版本? update修改必须是最新版本,select查找不会修改,所以可查当前记录或者历史版本。事务即使没有提交,修改操作就是将数据修改了,已经形成了历史版本链。

修改数据时修改的都是最新的版本,RR可重复读级别就是另一个事务看到的版本和我修改的版本并不一样,看到的是老版本,一种隔离性的实现理解。隔离级别的本质:让你看到哪一个历史版本

读取最新的记录叫当前读,读取之前的版本叫做快照读。增删改,都叫做当前读,select也有可能当前读,只是不形成版本罢了。

在多个事务同时删改查的时候,都是当前读,是要加锁的。那同时有select过来,如果也要读取最新版(当前读),那么也就需要加锁,这就是串行化。但如果是快照读,读取历史版本的话,是不受加锁限制的 ,因为能访问快照读的一定是select,已经是历史版本就说明是不可修改的了。换言之,提高了效率,即MVCC的意义所在。

  • 那为什么要有隔离级别呢? 事务都是原子的。所以,无论如何,事务启动之后,ID都是被确定的了,事务总有先有后。

那么多个事务在执行中,CURD操作是会交织在一起的。那么,为了保证事务的“有先有后”,是不是应该让不同的事务看到它该看到的内容,这就是所谓的隔离性与隔离级别要解决的问题。

  • 那么,如何保证,不同的事务,看到不同的内容呢?也就是如何实现隔离级别?

ReadView

mysql会为事务管理形成的结构体,里面有一个指针指向另一个对象,就是readview。

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

Read View 在 MySQL 源码中,就是一个类,本质是用来进行可见性判断的。 即当我们某个事务执行快照读的时候,对该记录创建一个 Read View 读视图,把它比作条件,用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的 undo log 里面的某个历史版本的数据。 事务在存在并发执行的。

class Readview
{
    m_ids; //一张列表,用来维护Read View生成时刻,系统正活跃的事务ID,并发中的。
    up_limit_id; //记录m_ids列表中事务ID最小的ID(没有写错) 低水位
    low_limit_id; //ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1(也没有写错) 高水位
    creator_trx_id //创建该ReadView的事务ID 
};

在这里插入图片描述

我们在实际读取数据版本链的时候,是能读取到每一个版本对应的事务ID的,即:当前记录的 DB_TRX_ID

那么,我们现在手里面有的东西就有,当前快照读的 ReadView 和 版本链中的某一个记录的 DB_TRX_ID

所以现在的问题就是,当前快照读,应不应该读到当前版本记录。用上下limit_id和链表中的修改版本的事务id进行对比,就能保证我的可见性。李白的诗词我看的到,我孙子写的我现在还看不到。我看到的一定是之前已经有的。当你的事务到来的时候,形成事务ID,你能看到的内部的各种数据,就要被确定下来。

实验

创建一张表,然后先后有四个事务几乎同时开始进行访问这张表。

在这里插入图片描述

当事务2进行快照读,数据库为他生成了一个readview读视图如下:

m_ids;		//1,3
up_limit_id; //1
low_limit_id;//4+1=5,因为这个是系统指定的readview生成时刻尚未分配的下一个事物的id
creator_trx_id;//2

只有事务4修改过记录,并且是在事务2 形成快照读之前就提交了事务。

在这里插入图片描述

事务2在快照读该记录时会拿着事务4的DB_TRX_ID和up_limit_id,low_limit_id和活跃事务列表(trx_list)进行比较确定事务2能看到的该记录的版本。而事务4提交的记录对应的事务id DB_TRX_ID=4;

4>1&&4<5,所以事务4并不是在我形成快照前后提交的,只能是在我形成快照时提交的,m_ids中也没有4,说明事务4不在当前的活跃事务列表中。

所以,事务4应该看到,并且是事务2能读到的最新的数据版本记录,也是全局角度最新的版本。

  • 以上实验是RC级别,并行的事务提交了你能看到。
RR和RC本质区别

并不是在事务begin的时候创建readview,而是在首次select之后才开始创建readview,undolog前后创建的区别导致能不能看到修改的内容也就是历史版本的在两个事物创建readview先后问题。

两个事务分别select,底层此时分别为他俩各自形成快照读。

例一:实现效果是RR级别
事务A操作事务A描述事务B描述事务B操作
begin开启事务开启事务begin
select*from stu快照读快照读select*from stu
update age=18更新值age=18
commit提交事务
快照读B看到的是age并未更新(之前形成的readview确定了已经)select*from stu
看当前读,才看到age=18或者先把Bcommit再快照读不过是新的了select *from stu lock in share mode
例2:实验效果是RC级别
事务A操作事务A描述事务B描述事务B操作
begin开启事务开启事务begin
select*from stu快照读
update age=28更新值age=28
commit提交事务
select快照读age=28select*from stu
看当前读,才看到age=28select *from stu lock in share mode

结论:

  1. 正是事务B的Read View生成时机的不同,从而造成RC,RR级别下快照读的结果的不同。
  2. 在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照及Read View, 将当前系统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见。本质就是不对压入undolog中的位置进行更新了。
  3. 而在RC级别下的,事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因,总之在RC隔离级别下,是每个快照读都会生成并获取最新的Read View;本质就是持续对Readview的位置进行更新。
  4. 而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
  5. 正是RC每次快照读,都会形成Read View,所以,RC才会有不可重复读问题,因为每次看到的东西可能都不一样,或做出修改的或是没做出修改的造成不可重复读问题。

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

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

相关文章

机器学习基础概念篇 | 10大经典算法

“数据算法模型”。 面对具体的问题&#xff0c;选择切合问题的模型进行求解十分重要。有经验的数据科学家根据日常算法的积累&#xff0c;往往能在最短时间内选择更适合该问题的算法&#xff0c;因此构建的模型往往更准确高效。本文归纳了机器学习的10大算法&#xff0c;并分别…

FIX:WIN11客户机完美支持 Eyeshot Fem 最新Crack

概述 将 CAD 功能添加到您的 .NET 应用程序 Eyeshot 是一个基于.NET Framework 的CAD 控件。它允许开发人员快速将 CAD 功能添加到 WinForms 和 WPF 应用程序。Eyeshot 提供了从头开始构建几何体、使用有限元方法对其进行分析并在其上生成刀具路径的工具。还可以使用 CAD 交换…

Ubuntu安装Tango教程

文章目录环境步骤其他指令参考环境 虚拟机&#xff1a;VMware Ubuntun&#xff1a;20.04LTS Tango&#xff1a;9.3.4 步骤 为tango-controls安装: sudo apt-get install g openjdk-8-jdk mariadb-server libmariadb-dev zlib1g-dev libomniorb4-dev libcos4-dev omniidl li…

RPM包制作

如何准备编译制作一个RPM包 编译制作RPM包&#xff0c;主要包含如下几个步骤: 规划好制作一个什么样的RPM包 搜集相关的软件和压缩包、补丁 创建SPEC文件 制作RPM包 测试验证RPM包规划好制作一个什么样的RPM包 一般情况下&#xff0c;有如下几种情况会需要制作RPM包 应用程…

MySQL中Innodb 存储引擎的Buffer Pool详解

MYSQL的InnoDB存储引擎为了提高性能&#xff0c;减少磁盘IO&#xff0c;而设计了缓冲池&#xff08;Buffer Pool&#xff09;。结构图如下&#xff1a; Buffer Pool 什么是Buffer Pool Buffer Pool即缓冲池&#xff08;简称BP&#xff09;&#xff0c;BP以Page页为单位&#x…

详解Lombok 的使用,工作原理,优缺点

文章目录Lombok概述Lombok的安装Lombok的具体使用Lombok工作原理Lombok的优缺点Lombok概述 相信大家对于lombok应该都不陌生&#xff0c;Lombok是一个可以大幅减少java模板代码的工具。通俗一点来说&#xff0c;通过添加注解的方式&#xff0c;不需要为类编写常用几个方法&…

stm32f407VET6 系统学习 day05 复位, 时钟,看门狗, 滴答定时器

1. 复位 1.三种类型的复位&#xff0c;系统复位&#xff0c;电源复位&#xff0c;备份复位; 2.复位作用:让程序从头开始运行&#xff0c;恢复到一开始运行的状态 2.stm32 时钟源 1. 5个是时钟源 1.HSI高速内部时钟:RC振荡器&#xff0c;频率为16MHz&#xff0c;精度不高。可…

npm中dependencies与devDependencies的区别

这个问题的出现是我下载npm包中的依赖文件安装错地方了&#xff0c;导致上线项目有问题&#xff0c;顺便一起来看看它们的区别。 一、前言 说白了就是当初学的时候太菜&#xff0c;没注意到它们的区别&#xff0c;也没去查明白。哈哈哈 二、dependencies与devDependencies的区别…

【人工智能与机器学习】——深度学习(学习笔记)

&#x1f4d6; 前言&#xff1a;长期以来&#xff0c;图像识别技术一直是人工智能研究领域的难题。近年来&#xff0c;随着算力的提升、物联网与大数据的出现、机器学习算法的快速发展&#xff0c;科学家们终于找到了有效的方法来实现图像识别&#xff0c;这就是基于人工神经网…

【OpenCV-Python】教程:8-2 图像修复 Image Inpainting

OpenCV Python 图像修复 【目标】 去除小噪声和笔画等&#xff1b; 【理论】 大多数人家里都会有一些旧照片&#xff0c;上面有一些黑点&#xff0c;一些笔画等。你想过把它修复回来吗?我们不能简单地在油漆工具中删除它们&#xff0c;因为它只会用白色结构取代黑色结构&a…

代码随想录拓展day4 143.重排链表;141. 环形链表;面试题 02.07. 链表相交

代码随想录拓展day4 143.重排链表&#xff1b;141. 环形链表&#xff1b;面试题 02.07. 链表相交 关于链表的一些应用&#xff0c;基本都用到了快慢指针的思路。对于单链表来说&#xff0c;确定边界&#xff0c;也就是遍历时的终止条件非常重要。 143.重排链表 143. 重排链表…

Web前端105天-day65-ToolChain

ToolChain01 目录 前言 一、Webpack 二、指南 总结 前言 ToolChain01学习开始 一、Webpack 官网&#xff1a;webpack 浏览器仅支持: html css 和 js 三种语言实际开发中: 会使用到其他的一些语言, 例如 TS, sass, scss 等.... 这些语言开发起来更加方便快捷, 但是浏览器不…

pinia 笔记

1、安装 npm i pinia -S2、创建store基本结构 1、在src下创建store文件夹并创建app.js文件&#xff0c;同时编写基本代码结构 // 引入实例化store的函数 import { defineStore } from "pinia";// 实例出一个名为app的store,那appStore是什么&#xff1f;它代表当前…

【SpringMVC】SpringMVC实现文件上传

1.一般的文件上传 1.1 文件上传的必要前提 form 表单的 enctype 取值必须是&#xff1a;multipart/form-data(默认值是:application/x-www-form-urlencoded) enctype:是表单请求正文的类型 method 属性取值必须是 Post 提供一个文件选择域<input type"file" /&…

代码随想录二刷day2

代码随想录复习 文章目录代码随想录复习209.长度最小的子数组&#xff08;滑动窗口&#xff09;76.最小覆盖子串904.水果成篮59.螺旋矩阵2螺旋矩阵1209.长度最小的子数组&#xff08;滑动窗口&#xff09; 209.长度最小的子数组 复习一下滑动窗口&#xff0c;滑窗的复杂度还是…

MySQL时间查询讲解+实战教学(查询本月、上个月、下个月等等的数据)

MySql时间查询 MySql查询当前时间 查询 年-月-日 时:分:秒 select now() 查询 年-月-日 select DATE(CURDATE()) 查询 年-月 select date_format(NOW(),%Y-%m) 查询当前年 select YEAR(CURDATE()) 查询当前月 select MONTH(CURDATE()) 查询当前日 select DAYOFMONTH(NOW()) 查…

【论文简述】Efficient Multi-view Stereo by Iterative Dynamic Cost Volume(CVPR 2022)

一、论文简述 1. 第一作者&#xff1a;Shaoqian Wang、Bo Li 2. 发表年份&#xff1a;2022 3. 发表期刊&#xff1a;CVPR 4. 关键词&#xff1a;MVS、深度学习、动态代价体、GRU、迭代优化 5. 探索动机&#xff1a;由于正则化步骤需要较多的GPU内存和处理时间&#xff0c…

大话JMeter4|不同的并发数可以自动化做压测吗?

1080709 23.5 KB 上节课爱画漫画的小哥哥用漫画形式向大家展示了JMeter的进阶用法&#xff1a;如何搭建InfluxDB&#xff0c;使用更炫酷的Grafana。 看到很多小伙伴觉得看的不过瘾&#xff0c;在强烈的催促下&#xff0c;小哥哥的新文章又出来了。这次小哥哥又给我们带来怎样的…

vue + nodejs + npm

node.js下载 1、如图所示&#xff1a; 2、建立node_cache、node_global文件夹&#xff1a; 然后运行以下2条命令 npm config set prefix “D:\node-v14.15.0-win-x64\node_global” npm config set cache “D:\node-v14.15.0-win-x64\node_cache” 执行npm list -global查看&…

编译原理——求短语、直接短语(简单短语)、素短语、句柄

先介绍一下短语、直接短语&#xff08;简单短语&#xff09;、素短语、句柄怎么求&#xff1a;这个图是核心 然后通过一些例题&#xff0c;实战一下&#xff1b; 根据上面介绍的概念、求法&#xff0c;应用一下即可&#xff1b; 例题1 短语&#xff1a;注意对于每一个子树&a…