【MySQL篇】事务管理,事务的特性及深入理解隔离级别

目录

一,什么是事务

二,事务的版本支持

 三,事务的提交方式

四,事务常见操作方式 

五,隔离级别

1,理解隔离性

 2,查看与设置隔离级别

3,读未提交(read uncommitted)

4,读提交(read committed)

5,可重复读(repeatable read)

 ​编辑

6,串行化(serializable)

7,总结 

六,深入理解隔离级别

MVCC的实现原理 

3个隐藏记录字段

undo日志 

模拟MVCC

Read View 

总结 

RR与RC的本质区别


一,什么是事务

先思考一个场景,在一个火车票购票系统中,西安到郑州的火车票只剩下一张,两个用户进行买票,可以想象成这两个用户对表中数据进行CURD,而如果这些CURD不加控制,会不会出现问题?

当客户端A检查到还有一张票时,将票卖掉,还没有 执行更新数据库的语句时,客户端B检查了票数,发现大于0,于是又买了一次票。然后 A将票数更新回数据库。这就出现了同一张票被买了2次的问题。

为了解决上述的问题,就需要CURD操作满足 如下的属性:

1,买票的过程需要是原子的

2,买票之间不能互相影响

3,买完票,对数据进行修改后,数据应该永久有效

4,买前和买后的状态都是确定的


 事务的概念:事务就是一组DML语句组成,这些语句在逻辑上 存在相关性,这一组DML语句要么全部成功 ,要么全部失败,是一个整体。

通俗的讲,事务就是一些SQL语句的组合。在业务层面上,为了完成某个业务,比如完成一次转账,要将一个用户的数据减100,另一个用户的数据加100,为了完成这个业务,在MySQL层面上,就需要多条SQL语句来完成。

单独的一条SQL语句之间是没有关系的,但是站在使用者的角度,这些SQL语句是 存在逻辑关系的,共同完成某个任务。这些SQL语句要么全部成功,要么全部失败。就比如转账的例子,不能使一个用户减100,后面的操作失败,导致另一个 用户没有加100.


事务就是要做或者所做的事情 ,主要用于处理操纵量大,复杂度高的数据。一个MySQL数据库,可不止一个事务在运行。在同一时刻,可能有大量的请求被包装成事务,向MySQL服务器发起事务处理请求。而每条事务至少一条SQL语句,也可能有很多条SQL语句,这样如果大家都访问同样的表数据,在不加保护的情况下,一定会存在问题。也会存在,一个事务中,一些SQL语句执行完成了,另一些SQL执行出错或者不再执行的情况,那么这种情况下该怎么办?

所以,一个完整的事务,绝对不是简单的SQL语句的集合,还需要满足如下属性:

  • 原子性:一个事务(transaction)中的所有操作,要么全部完成 ,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像 这个事务从来没有执行过。
  • 一致性:在事务开始之前和事务结束之后,数据库的完整性没有被破坏。这表示事务按照我们的预期执行的。
  • 隔离性:数据库允许多个并发事务同时对其数据进行读和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致的问题。事务隔离分为不同的级别:读未提交(read uncommited),读提交(read commited),可重复读(repeatable read )和串行化(serializable)。
  • 持久性:事务结束后,对数据的修改是永久的,即便系统 故障也不会丢失。

上面的四个属性,可以简称为ACID:

原子性(Atomicity)

一致性(Consistency)

隔离性(Isolation)

持久性(Durability)

二,事务的版本支持

在MySQL中只有使用了Innodb存储引擎的数据库或表才支持事务,MyISAM不支持。

查看数据库引擎

show engines;   --表格显示

show engines\G  --行显示

 三,事务的提交方式

事务的提交方式有两种:自动提交和手动提交

查看事务提交方式

show variables like 'autocommit'

用set来改变MySQL的自动提交模式:

四,事务常见操作方式 

创建测试表 

 mysql> create table account(

mysql> id int  primary key,

mysql> name varchar(20) not null default '',

mysql> blance decimal(10,2) not null default 0.0);

演示:事务的开始与回滚 

 begin 开始一个事务,commit结束这个事务。

如果事务执行到一半,未commit,客户端崩掉,MySQL会自动回滚到开始。

如果事务执行完了,commit了,客户端崩掉,MySQL数据不受影响,已经持久化。

begin开始一个事务,需要手动commit,和MySQL是否开启自动提交无关。

单条SQL与事务的关系:单条SQL也是一个事务,被MySQL封装成一个事务。


总结:

  • 只要输入begn或者start transaction,事务便必须要通过commit提交,才会持久化,与是否支持自动提交无关(autocommit)。
  • 事务可以手动回滚,同时,当操作异常,MySQL会自动回滚。
  • 对于Innodb,每一条SQL语句,都会封装成事务 ,自动提交。(select有特殊情况 ,因为MySQL有MVCC)。

五,隔离级别

1,理解隔离性

mysql可能会被多个客户端进程(线程)进行访问,访问的方式以事务的方式进行

一个事务 可能由多条SQL语句构成,也就意味着,任何一个事务,都有执行前,执行中,执行后的阶段。而所谓的原子性,就是让用户层,要么看到执行前,要么看到执行后。执行中出现问题,可以随时回滚。所以单个事务,对用户表现出来的就是原子性的。

那么在多个事务执行自己的SQL时,就有可能出现相互影响的情况。比如:多个事务访问同一张表,甚至同一行数据。

数据库中,为了保证事务执行过程中尽量不受干扰,就有了隔离性的概念。

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


隔离级别:

读未提交(read uncommitted):在该隔离级别下,所有事务都可以看到其他事务还没有提交的执行结果,(实际生产中不可能使用这种级别),相当于没有隔离性,会有很多并发问题,如脏读,幻读,不可重复读等问题。

读提交(read committed):该隔离级别是大多数数据库的默认隔离级别(不是MySQL的)。它满足了隔离的简单定义:一个事务只能看到其他已提交的事务所作的改变。这种隔离级别会引起不可重复读的问题,即一个事务执行时,如果多次select,会得到不同的结果。

可重复读(repeatable read):这是MySQL的默认级别,它确保同一个事务,在执行中,多次读取数据时,读到的是同一份数据。但是会有幻读的问题。

串行化(serializable):这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题,它在每个读的数据行上面加上共享锁。但是可能会导致超时和锁竞争。(这种隔离级别太极端,实际生产一般不使用)

接下来是对这4中隔离级别的现象演示。

 2,查看与设置隔离级别

查看

mysql> select @@global.transaction_isolation; --查看全局隔离级别

   

 mysql> select @@transaction_isolation;  --查看当前会话的隔离级别

会话启动时,当前会话的隔离级别会从全局隔离级别中拷贝一份。 

设置

语法:

set [session | global] transatcion isolation level [隔离级别];

设置当前会话的隔离级别

mysql> set session transaction isolation level serializable; -- 串行化

设置全局隔离级别: 

mysql> set global transaction isolation level read uncommitted;

//transaction 事务

//isolation 隔离

//level 级别

//read uncommitted 读未提交

  全局隔离级别设置好以后,需要重启终端才可以生效。 

mysql> select @@transaction_isolation;

 

3,读未提交(read uncommitted)

 现象演示:(先将当前隔离级别设置为read uncommitted)

一个事务在执行中,读到另一个执行中事务的更新(或其他操作)但是未commit的数据,这种现象叫做脏读。

4,读提交(read committed)

终端Acommit之后 :

 但是,终端B事务还未结束,那么就造成了,同一个事务内,同样的读取,在不同的时间段,读到了不同的数据,这种现象,叫做不可重复读。

5,可重复读(repeatable read)

 

 两个终端A和B,如果终端A在对应事务中insert数据,在终端B的事务周期中 ,没什么影响,也符合可重复的特点。但是,一般的数据库在可重复读的时候,无法屏蔽其他事务的insert操作(为什么?因为隔离性是对数据加锁完成的,而insert待插入的数据并不存在,那么一般加锁无法屏蔽insert数据)。会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读的情况下被读出来,导致多次查找时,会多查找除出的记录,就如同产生了幻觉,这种现象叫做幻读。但是,MySQL在repeatable read隔离级别下解决了该问题。

6,串行化(serializable)

对于所有操作全部加锁,进行串行化,不会出现问题。但是效率很低,几乎不会被采用。

 

7,总结 

隔离级别越高,安全性越高,但数据库并发性越低,往往需要在两者之间找一个平衡点。

不可重复读的重点是修改和删除:同样的条件,不同的时间,发现读取到的数据不一致。

幻读的终点在于新增:同样的条件,第一次和第二次读出来的数据记录不同。

六,深入理解隔离级别

数据库并发的场景有三种:

读-读:不存在任何问题,也不需要并发控制

写-写:有线程安全问题,可能会存在更新丢失问题。

读-写:有线程安全问题,可能会造成事务隔离性问题,如脏读,幻读,不可重复读等问题。

多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发机制。

首先抛出概念:

MVCC的全称是Multi-Version Concurrency Control,即多版本并发控制。它的主要目的是在读取数据时不加锁,从而提高并发性能。传统的锁机制可能会导致读阻塞写或者写阻塞读,而MVCC通过保存数据的历史版本来让读写操作可以同时进行,避免阻塞。

首先,每个事务都有一个唯一的事务ID,这个ID是在事务开始时分配的,按照时间顺序递增,就像每一个进程都有唯一一个PID。


MVCC的实现原理 

 理解MVCC的三个前置知识:

  • 3个隐藏记录字段
  • undo日志
  • Read View

3个隐藏记录字段

  1. DB_TRX_ID:6byte,最近修改(修改/插入)的事务ID,记录创建这条记录或者修改这条记录的的事务ID。
  2. DB_ROLL_PTR:7byte,回滚指针,这想这条记录的上一个版本。(这些数据一般在undo log中)。
  3. DB_ROW_ID:6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,Innodb会自动以DB_ROW_ID产生一个聚簇索引。
  4. 补充:实际还有一个删除flag隐藏字段,既记录被删除并不代表真的被删除,而是flag由0置1,或由1置0。

索引特性传送门:【MySQL篇】索引特性,索引的工作原理以及索引的创建与管理_mysql索引b+树特性-CSDN博客

undo日志 

首先,要理解一个事情,MySQL将来是以服务进程的方式,在内存中运行。索引,事务,隔离性等机制,都是在内存中完成的。即在MySQL内部的相关缓冲区中,保存相关数据,完成各种判断操作,然后再合适的时候数据刷新到磁盘上。

所以,undo日志可见简单的理解成,是一段MySQL内部的缓冲区,用来保存日志数据的。

重点会在下面模拟的时候讲解。

模拟MVCC

假设现在有一个事务,事务ID为11,对上面表的数据进行修改,将name(张三)改成name(李四)。大致过程如下:

  • 事务11,因为要修改,先对这条记录加行锁。
  • 修改前,先将该行记录拷贝到undo log中,所以undo log中就有了一行数据。
  • 现在修改MySQL中的记录,将原始记录name(张三)修改为name(李四)。并且修改原始记录的隐藏字段,DB_TRX_ID为当前事务的ID11,回滚指针列DB_ROLL_PTR,里面写入放入undo log中修改前数据的地址。从而指向上一条记录,即指向上一个版本。
  • 事务11提交,释放锁。

 

假设又有一个事务,事务ID为12,要将age改为30,同样按照上述过程。

总结:当一行数据被修改时,Innodb会将当前行的当前版本复制到undo log日志中,并通过DB_ROLL_PTR形成一个版本链。这样,每次修改都会都会形成一个新版本,旧版本保留在undo log中,直到不再需要为止。

上面是以更新(update)为例,如果是删除(delete)操作呢?

其实是一样的,因为上面提到过,还有一个隐藏字段flag,删除一段记录,设置flag即可。因此也可以生成版本链。


如果是insert插入数据呢?

因为insert是插入数据,之前没有数据,那么insert也就没有历史版本。但是,一般为了回滚操作,insert后的数据也是要被放到undo log日志中的。

如果当前事务commit了,那么这个undo log的历史insert记录就可以被清空了。因为一个事务跑起来了,插入数据,这个数据之前是没有的,就注定了和该事务并发运行的事务是不会用这个数据的,所以insert形成的版本链就可以清空了。

而update和delete就不一定要被清空了,因为这条事务提交了,可能会有其他事务也要访问这条记录。


如果是select操作呢? 

select是不会对任何数据进行修改的。所以,维护select多版本是没有意义的。


select读取,应该读取到的是最新版本,还是历史版本?

当前读:读取最新的记录。增删改,都是当前读。

快照读:读取历史版本。

在多个事务进行当前读的时候,同时多个select,是需要加锁的,这就是串行化。

而对于读提交和可重复读的隔离级别,是在进行快照读,读取历史版本。

但如果是快照读,读取历史版本的话,是不受加锁限制的。也就是可以并发执行!换言之,调高了效率,这就是MVCC的意义。


但是,什么决定了select是当前读,还是快照读呢?

隔离级别。

事务是有执行前,执行中和执行后三个阶段的。begin-CURD-commit。

那么多个事务执行中,CURD是会交织子在一起的。那么,为了保证事务有先有后,就该让不同的事务看到该看到的内容,这就是隔离级别要完成的任务。这个工作就由接下来的read view实现。

Read View 

概念:

Read View 就是事务进行快照读操作时产生的读视图,在该事务执行快照读的那一刻,记录并维护当前活跃事务的ID,这个ID是自增的,所以最新的事务,ID值越大。

Read View作用:事务进行读操作的时候,确定那些数据版本对该事务可见。


 事务和Read View的关系,可以理解成进程PCB和进程地址空间的关系。让该事务看到应该看到的数据版本。

Read View在MySQL源码中,就是一个类,本质是用来进行可见性判断的。即当我们某个事务进行快照读的时候,读该记录创建一个Read View读视图,用它来判断当前哪个事务可以看见哪个版本的数据,可能是当前最新的数据,也就能是该记录的undo log日志里面的某个版本数据。


Read View类中几个重要的字段:

 m_ids;           //一张列表,用来维护Read View生成时刻,系统中活跃的事务ID

up_limit_id;    //记录m_ids列表中事务ID最小的ID

low_limit_id;  //Read View生成时刻,系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1

creator_trx_id;   //创建该事务的事务ID

 对于undo log中的数据版本链,我们在读取的时候,是能读取到每一个版本的事务ID的,即DB_TRX_ID。

所以,现在我们手里有两样东西:当前事务的Read View和数据版本链中的某一条记录的DB_TRX_ID。

所以,现在的问题就是当前事务应不应该看到当前版本记录。

总结 

MVCC实现原理:

核心组件作用
事务ID每个事务启动时,分配一个事务ID,ID是自增的
undo 日志存储数据的历史版本,形成版本链。
Read View事务进行读操作时生成,确定那些版本对该事务可见
隐藏字段每个记录包含3个隐藏字段

MVCC的优缺点:

优点缺点
非阻塞读,提高并发性能需要维护undo日志,增加存储开销
减少锁冲突,降低死锁概率旧版本数据不及时清理可能影响性能
支持快照图,实现一致性视图复杂事务可能导致版本链过长

RR与RC的本质区别

有了MVCC的认识,接下来看看mysql是如何实现RC和RR两种不同的隔离级别的。

注意:read view是事务可见性的一个类,不是事务创建出来,就会有 read view,而是当事务第一次进行快照读的时候,mysql才会生成read view。

隔离级别read view 生成时机效果
读已提交(RC)

每次查询时生成新的read view,

可能 一直在变化。

能看到其他事务提交后的修改。
可重复读(RR)

事务首次查询生成read view,

之后就不会变化了。

始终看到事务开始时的数据快照,避免不可重复读的问题。

 

RR 级别下,快照读生成 Read View 时, Read View 会记录此时所有其他活动事务的快照,这些事
务的修改对于当前事务都是不可见的。而早于 Read View 创建的事务所做的修改均是可见的。
而在 RC 级别下的,事务中,每次快照读都会新生成一个快照读 Read View, 这就是我们在 RC 级别下 的事务中可以看到别的事务提交的更新的原因。

正式因为RC每次快照读,都会形成新的read view,所以,RC才会有不可重复读的问题。

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

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

相关文章

C++Primer学习(14.1 基本概念)

当运算符作用于类类型的运算对象时,可以通过运算符重载重新定义该运算符的含义。明智地使用运算符重载能令我们的程序更易于编写和阅读。举个例子,因为在Sales_item类中定义了输入、输出和加法运算符,所以可以通过下述形式输出两个Sales_item…

循相似之迹:解锁协同过滤的核心推荐逻辑

目录 一、引言二、协同过滤的基本原理三、协同过滤的算法类型(一)基于用户的协同过滤(二)基于物品的协同过滤 四、协同过滤的应用案例(一)电商平台的商品推荐(二)音乐平台的歌曲推荐…

RuoYi基础学习

1 若依搭建 前后端分离版本:RuoYi-Vue利用SpringBoot作为后端开发框架,与Vue.js结合,实现了前后端分离的开发模式。这种架构有助于提高开发效率,前后端可以独立开发和部署,更适合现代化的Web应用开发。 RuoYi-Vue3&a…

Docker 安装部署Harbor 私有仓库

Docker 安装部署Harbor 私有仓库 系统环境:redhat x86_64 一、首先部署docker 环境 定制软件源 wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repoyum install -y yum-utils device-mapper-persistent-data lvm2…

【Basys3】外设-灯和数码管

灯 约束文件 set_property PACKAGE_PIN W5 [get_ports CLK] set_property PACKAGE_PIN U18 [get_ports rst] set_property PACKAGE_PIN U16 [get_ports {led[0]}] set_property PACKAGE_PIN E19 [get_ports {led[1]}] set_property PACKAGE_PIN U19 [get_ports {led[2]}] set…

【Django】教程-1-安装+创建项目+目录结构介绍

欢迎关注我!后续会更新django教程。一周2-3更,欢迎跟进,本周会更新第一个Demo的单独一个模块的增删改查【Django】教程-4-一个增删改查的Demo【Django】教程-2-前端-目录结构介绍【Django】教程-3-数据库相关介绍 1.项目创建 1.1 安装 Djan…

蓝桥杯 之 二分

文章目录 习题肖恩的n次根分巧克力2.卡牌 二分是十分重要的一个算法,常常用于求解一定范围内,找到满足条件的边界值的情况主要分为浮点数二分和整数二分二分问题,最主要是写出这个check函数,这个check函数最主要就是使用模拟的方法…

SpringBoot集成腾讯云OCR实现身份证识别

OCR身份证识别 官网地址&#xff1a;https://cloud.tencent.com/document/product/866/33524 身份信息认证&#xff08;二要素核验&#xff09; 官网地址&#xff1a;https://cloud.tencent.com/document/product/1007/33188 代码实现 引入依赖 <dependency><…

2025年3月电子学会c++五级真题

结绳 #include <bits/stdc.h> using namespace std;int n,a[10010];int main() {cin>>n;for(int i 0;i<n;i){cin>>a[i];}sort(a0,an);//将a数组从小到大排序double sum 0;for(int i 0;i<n;i){sum (suma[i])/2;}cout<<(int)sum;return 0; } 最…

Typora使用Gitee作为图床

Typora使用Gitee作为图床 文章目录 Typora使用Gitee作为图床Gitee准备图床仓库下载安装软件安装插件 配置Typora Gitee准备图床仓库 新建一个仓库右上角下拉->设置->安全设置->私人令牌->生成新令牌&#xff0c;注意将令牌保存&#xff08;只会出现一次&#xff0…

QT音乐播放器(1):数据库保存歌曲

实现功能&#xff1a;用数据库保存本地导入和在线搜索的歌曲记录 目录 一. 保存本地添加的歌曲 1. 使用QSettings &#xff08;1&#xff09;在构造函数中&#xff0c;创建对象。 &#xff08;2&#xff09;在导入音乐槽函数中&#xff0c;保存新添加的文件路径&#xff0c…

SQLAlchemy关键词搜索技术深度解析:从基础过滤到全文检索

在数据驱动的应用开发中&#xff0c;基于关键词的模糊查询是常见的业务需求。SQLAlchemy作为Python生态中最流行的ORM框架&#xff0c;提供了多种实现关键词搜索的技术方案。本文将从性能、适用场景和技术复杂度三个维度&#xff0c;系统对比分析SQLAlchemy中关键词搜索的最佳实…

css属性列举

介绍 CSS word-spacing 属性&#xff0c;用于指定段字之间的空间&#xff0c;例如&#xff1a; p {word-spacing:30px; }word-spacing属性增加或减少字与字之间的空白。 注意&#xff1a; 负值是允许的。 浏览器支持 表格中的数字表示支持该属性的第一个浏览器版本号。 属…

python实现股票数据可视化

最近在做一个涉及到股票数据清洗及预测的项目&#xff0c;项目中需要用到可视化股票数据这一功能&#xff0c;这里我与大家分享一下股票数据可视化的一些基本方法。 股票数据获取 在经过多次尝试后&#xff0c;发现了一个

从 JDK 11 到 JDK 17:OpenRewrite 实战 Spring Boot 升级指南

一、为什么选择 OpenRewrite 升级&#xff1f; 在 Spring Boot 项目升级 JDK 的过程中&#xff0c;我们面临两个核心痛点&#xff1a; 语法兼容性问题&#xff08;如废弃的 API、新的关键字&#xff09;依赖版本冲突&#xff08;特别是 Spring Boot 与 JDK 版本的匹配&#x…

交换技术综合实验

一、实验拓扑 二、实验要求 内网IP地址使用172.16.0.0/16分配。 SW1和SW2之间互为备份。 VRRP/STP/VLAN/Eth-trunk均使用。 所有PC通过DHCP获取IP地址。 ISP只能配置IP地址。 所有电脑可以正常访问ISP路由器。 三、实验步骤 基于172.16.0.0/16进行划分 172.16.2.0/24&…

【Linux】了解基础指令(超详细)

目录 【whoami】指令【pwd】指令【mkdir】指令【touch】指令【ls】指令文件的扩展内容 【cd】指令相对路径和绝对路径(.和..存在的原因)绝对路径相对路径 【rm】指令【man】命令【less】指令echo指令重定向操作追加重定向 cat 指令输入重定向 管道操作(组合指令)查找三剑客find…

基于改进粒子群算法的多目标分布式电源选址定容规划(附带Matlab代码)

通过分析分布式电源对配电网的影响&#xff0c;以有功功率损耗、电压质量及分布式电源总容量为优化目标&#xff0c;基于模糊理论建立了分布式电源在配电网中选址定容的多目标优化模型&#xff0c;并提出了一种改进粒子群算法进行求解。在算例仿真中&#xff0c;基于IEEE-14标准…

26_ajax

目录 了解 接口 前后端交互 一、安装服务器环境 nodejs ajax发起请求 渲染响应结果 get方式传递参数 post方式传递参数 封装ajax_上 封装ajax下 了解 清楚前后端交互就可以写一些后端代码了。小项目 现在写项目开发的时候都是前后端分离 之前都没有前端这个东西&a…