搜索
首页数据库mysql教程带你聊聊MySQL中的事务隔离

带你聊聊MySQL中的事务隔离

Aug 30, 2021 am 10:30 AM
mysql事务隔离

本篇文章带大家了解一下MySQL中的事务隔离,介绍一下事务的特性、隔离级别、事务启动的方式等,希望对大家有所帮助!

带你聊聊MySQL中的事务隔离

事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的,但并不是所有的引擎都支持事务。比如MySQL原生的MyISAM引擎就不支持事务。【相关推荐:mysql教程(视频)】

一、事务的特性

  • 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样
  • 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏
  • 隔离性:数据库允许多个并发事务同时对数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致
  • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失

二、隔离级别

1.当数据库上有多个事务同时执行的时候,就可能出现脏读、不可重复读、幻读的问题

  • 脏读:B事务读取到了A事务尚未提交的数据
  • 不可重复读:一个事务读取到了另一个事务中提交的update的数据
  • 幻读/虚读:一个事务读取到了另一个事务中提交的insert的数据

2.事务的隔离级别包括:读未提交、读提交、可重复读和串行化

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到(解决脏读,Oracle默认的隔离级别)
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的,而且未提交变更对其他事务也是不可见的(解决脏读和不可重复读,MySQL默认的隔离级别)
  • 串行化:对于同一行记录,写会加写锁,读会加读锁,当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行(解决脏读、不可重复读和幻读)

安全性依次提交,性能依次降低

3.假设数据表T中只有一列,其中一行的值为1

create table T(c int) engine=InnoDB;
insert into T(c) values(1);

下面是按照时间顺序执行两个事务的行为:

在这里插入图片描述

  • 若隔离级别是读未提交,则V1是2。这时候事务B虽然还没提交,但是结果已经被A看到了。V2、V3都是2
  • 若隔离级别是读提交,则V1是1,V2是2。事务B的更新在提交后才能被A看到。V3也是2
  • 若隔离级别是可重复读,则V1、V2是1,V3是2。之所以V2是1,遵循的是事务在执行期间看到的数据前后必须是一致的
  • 若隔离级别是串行化,V1、V2值是1,V3是2

在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在可重复读隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在读提交隔离级别下,这个视图是在每个SQL语句开始执行的时候创建的。读未提交隔离级别下直接返回记录上的最新值,没有视图概念;而串行化隔离级别下直接用加锁的方式来避免并行访问

三、事务隔离的实现(以可重复读为例)

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

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

在这里插入图片描述

当前值是4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的read-view。如图中看到的,在视图A、B、C里面,这一个记录的值分别是1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于read-viewA,要得到1,就必须将当前值一次执行图中所有的回滚操作得到

即使现在有另外一个事务正在将4改成5,这个事务跟read-view A、B、C对应的事务是不会冲突的

系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除

四、事务启动的方式

MySQL的事务启动方式有以下几种:

  • 显示启动事务语句,begin或start transaction。提交语句是commit,回滚语句是rollback
  • set autocommit=0,这个命令将这个线程的自动提交关掉。意味着如果只执行一个select语句,这个事务就启动了,而且不会自动提交事务。这个事务持续存在直到主动执行commit或rollback语句,或者断开连接

建议使用set autocommit=1,通过显示语句的方式来启动事务

可以在information_schema库中的innodb_trx这个表中查询长事务,如下语句查询持续时间超过60s的事务

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

五、事务隔离还是不隔离

下面是一个只有两行的表的初始化语句:

mysql> CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `k` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

事务A、B、C的执行流程如下,采用可重复读隔离级别
在这里插入图片描述

begin/start transaction命令:不是一个事务的起点,在执行到它们之后的第一个操作InnoDB表的语句,事务才真正启动,一致性视图是在执行第一个快照读语句时创建的

start transaction with consistent snapshot命令:马上启动一个事务,一致性视图是在执行这条命令时创建的

按照上图的流程执行,事务B查到的k的值是3,而事务A查到的k的值是1

1、快照在MVCC里是怎么工作的?

在可重复读隔离级别下,事务启动的时候拍了个快照。这个快照是基于整个库的,那么这个快照是如何实现的?

InnoDB里面每个事务有一个唯一的事务ID,叫做transaction id。它在事务开始的时候向InnoDB的事务系统申请,是按申请顺序严格递增的

每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把transaction id赋值给这个数据版本的事务ID,记作row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。也就是说,数据表中的一行记录,其实可能有多个版本,每个版本有自己的row trx_id

下图是一个记录被多个事务连续更新后的状态:

在这里插入图片描述

语句更新生成的undo log(回滚日志)就是上图中的是哪个虚线箭头,而V1、V2、V3并不是物理上真实存在的,而是每次需要的时候根据当前版本和undo log计算出来的。比如,需要V2的时候,就是通过V4依次执行U3、U2算出来的

按照可重复读的定义,一个事务启动的时候,能够看到所以已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。在实现上,InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前在启动了但还没提交的所有事务ID。数组里面事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为高水位。这个视图数组和高水位就组成了当前事务的一致性视图。而数据的可见性规则,就是基于数据的row trx_id和这个一致性视图的对比结果得到的

这个视图数组把所有的row trx_id分成了几种不同的情况

在这里插入图片描述

对于当前事务的启动瞬间来说,一个数据版本的row trx_id,有以下几种可能:

1)如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的

2)如果落在红色部分,表示这个版本是由将来启动的事务生成的,肯定不可见

3)如果落在黄色部分,那就包括两种情况

  • 若row trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见
  • 若row trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见

InnoDB利用了所有数据都有多个版本的这个特性,实现了秒级创建快照的能力

2、为什么事务A的查询语句返回的结果是k=1?

假设:

1.事务A开始时,系统里面只有一个活跃事务ID是99

2.事务A、B、C的版本号分别是100、101、102

3.三个事务开始前,(1,1)这一行数据的row trx_id是90

这样,事务A的是数组就是[99,100],事务B的视图数组是[99,100,101],事务C的视图数组是[99,100,101,102]

在这里插入图片描述

从上图中可以看到,第一个有效更新是事务C,从数据从(1,1)改成了(1,2)。这时候,这个数据的最新版本的row trx_id是102,而90这个版本已经成为了历史版本

第二个有效更新是事务B,把数据从(1,2)改成了(1,3)。这时候,这个数据的最新版本是101,而102又成为了历史版本

在事务A查询的时候,其实事务B还没提交,但是它生成的(1,3)这个版本已经变成当前版本了。但这个版本对事务A必须是不可见的,否则就变成脏读了

现在事务A要读数据了,它的视图数组是[99,100]。读数据都是从当前版本读起的。所以,事务A查询语句的读数据流程是这样的:

  • 找到(1,3)的时候,判断出row trx_id=101,比高水位大,处于红色区域,不可见
  • 接着,找到上一个历史版本,一看row trx_id=102,比高水位大,处于红色区域,不可见
  • 再往前找,终于找到了(1,1),它的row trx_id=90,比低水位小,处于绿色区域,可见

虽然期间这一行数据被修改过,但是事务A不论在什么时候查询,看到这行数据的结果都是一致的,我们称之为一致性读

一个数据版本,对于一个事务视图来说,除了自己的更新总是可见以外,有三种情况:

  • 版本未提交,不可见
  • 版本已提交,但是是在视图创建后提交的,不可见
  • 版本已提交,而且是在视图创建前提交的,可见

事务A的查询语句的视图数组是在事务A启动的时候生成的,这时候:

  • (1,3)还没提交,属于情况1,不可见
  • (1,2)虽然提交了,但是是在视图数组创建之后提交的,属于情况2,不可见
  • (1,1)是在视图数组创建之前提交的,可见

3、为什么事务B的查询语句返回的结果是k=3?

在这里插入图片描述

事务B要去更新数据的时候,就不能再在历史版本上更新了,否则事务C的更新就丢失了。因此,事务B此时的set k=k+1是在(1,2)的基础上进行的操作

更新数据都是先读后写的,而这个读,只能读当前的值,称为当前读。除了update语句外,select语句如果加锁,也是当前读

假设事务C不是马上提交的,而是变成了下面的事务C’,会怎么样?
在这里插入图片描述
上图中,事务C更新后没有马上提交,在它提交前,事务B的更新语句先发起了。虽然事务C还没提交,但是(1,2)这个版本也已经生成了,并且是当前的最新版本

这时候涉及到了两阶段锁协议,事务C没提交,也就是说(1,2)这个版本上的写锁还没释放。而事务B是当前读,必须要读最新版本,而且必须加锁,因此就被锁住了,必须等到事务C释放这个锁,才能继续它的当前读

在这里插入图片描述

七、事务的可重复读的能力是怎么实现的?

可重复读的核心就是一致性读;而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待

而读提交的逻辑和可重复读的逻辑类似,它们最主要的区别是:

  • 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图
  • 在读提交隔离级别下,每一个语句执行前都会重复算出一个新的视图

更多编程相关知识,请访问:编程视频!!

以上是带你聊聊MySQL中的事务隔离的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:csdn。如有侵权,请联系admin@php.cn删除
MySQL中有哪些不同的存储引擎?MySQL中有哪些不同的存储引擎?Apr 26, 2025 am 12:27 AM

mysqloffersvariousStorageengines,每个suitedfordferentusecases:1)InnodBisidealForapplicationsNeedingingAcidComplianCeanDhighConcurncurnency,supportingtransactionsancions and foreignkeys.2)myisamisbestforread-Heavy-Heavywyworks,lackingtransactionsactionsacupport.3)记忆

MySQL中有哪些常见的安全漏洞?MySQL中有哪些常见的安全漏洞?Apr 26, 2025 am 12:27 AM

MySQL中常见的安全漏洞包括SQL注入、弱密码、权限配置不当和未更新的软件。1.SQL注入可以通过使用预处理语句防止。2.弱密码可以通过强制使用强密码策略避免。3.权限配置不当可以通过定期审查和调整用户权限解决。4.未更新的软件可以通过定期检查和更新MySQL版本来修补。

您如何确定MySQL中的慢速查询?您如何确定MySQL中的慢速查询?Apr 26, 2025 am 12:15 AM

在MySQL中识别慢查询可以通过启用慢查询日志并设置阈值来实现。1.启用慢查询日志并设置阈值。2.查看和分析慢查询日志文件,使用工具如mysqldumpslow或pt-query-digest进行深入分析。3.优化慢查询可以通过索引优化、查询重写和避免使用SELECT*来实现。

如何监视MySQL Server的健康和性能?如何监视MySQL Server的健康和性能?Apr 26, 2025 am 12:15 AM

要监控MySQL服务器的健康和性能,应关注系统健康、性能指标和查询执行。1)监控系统健康:使用top、htop或SHOWGLOBALSTATUS命令查看CPU、内存、磁盘I/O和网络活动。2)追踪性能指标:监控查询每秒数、平均查询时间和缓存命中率等关键指标。3)确保查询执行优化:启用慢查询日志,记录并优化执行时间超过设定阈值的查询。

比较和对比Mysql和Mariadb。比较和对比Mysql和Mariadb。Apr 26, 2025 am 12:08 AM

MySQL和MariaDB的主要区别在于性能、功能和许可证:1.MySQL由Oracle开发,MariaDB是其分支。2.MariaDB在高负载环境中性能可能更好。3.MariaDB提供了更多的存储引擎和功能。4.MySQL采用双重许可证,MariaDB完全开源。选择时应考虑现有基础设施、性能需求、功能需求和许可证成本。

MySQL的许可与其他数据库系统相比如何?MySQL的许可与其他数据库系统相比如何?Apr 25, 2025 am 12:26 AM

MySQL使用的是GPL许可证。1)GPL许可证允许自由使用、修改和分发MySQL,但修改后的分发需遵循GPL。2)商业许可证可避免公开修改,适合需要保密的商业应用。

您什么时候选择InnoDB而不是Myisam,反之亦然?您什么时候选择InnoDB而不是Myisam,反之亦然?Apr 25, 2025 am 12:22 AM

选择InnoDB而不是MyISAM的情况包括:1)需要事务支持,2)高并发环境,3)需要高数据一致性;反之,选择MyISAM的情况包括:1)主要是读操作,2)不需要事务支持。InnoDB适合需要高数据一致性和事务处理的应用,如电商平台,而MyISAM适合读密集型且无需事务的应用,如博客系统。

在MySQL中解释外键的目的。在MySQL中解释外键的目的。Apr 25, 2025 am 12:17 AM

在MySQL中,外键的作用是建立表与表之间的关系,确保数据的一致性和完整性。外键通过引用完整性检查和级联操作维护数据的有效性,使用时需注意性能优化和避免常见错误。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器