MySQL进阶MySQL事务详解

Posted 小杨MiManchi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL进阶MySQL事务详解相关的知识,希望对你有一定的参考价值。

序号系列文章
5【MySQL基础】字符集与校对集详解
6【MySQL基础】MySQL单表操作详解
7【MySQL基础】运算符及相关函数详解
8【MySQL基础】MySQL多表操作详解

文章目录

前言

大家好,我是小杨!前面我为大家介绍完了MySQL中的基础知识,那么从今天开始我将为大家介绍MySQL中的进阶知识,那么今天我们就从MySQL中的事务这部分内容开始说起,希望大家能够收获多多!


1,事务概念

事务就是针对数据库的一组操作,它是由一条或多条SQL语句组成,且每个SQL语句之间是相互依赖的。

只要在程序执行过程中有一条SQL语句执行失败或发生错误,则其他的SQL语句都不会被执行。

简而言之,事务的执行要么成功,要么就返回至事务开始前的状态。

就比如,张三要给李四转账1000块钱,张三银行账户的钱减少1000,而李四银行账户的钱要增加1000。

转账流程可以大致分为三部分来完成:1,查询张三账户余额 2,张三账户余额-1000 3,李四账户余额+1000

只有这三个部分都完成才认为转账成功。

在数据库中,这个过程是使用三条SQL语句来完成的。

#1,张三账户余额查询
SELECT id,name,money FROM ccount WHERE name = '张三';

#2,张三账户减少1000元
UPDATE account SET money = money - 1000 WHERE name = '张三';

#3,李四账户增加1000元
UPDATE account SET money = money + 1000 WHERE name = '李四';

正常情况: 转账这个操作, 需要分为以下这么三步来完成 , 三步完成之后, 张三减少1000, 而李四增加1000, 转账成功 :

异常情况: 转账这个操作, 也是分为以下这么三步来完成 , 在执行第三步是报错了, 这样就导致张三减少1000块钱, 而李四的金额没变, 这就会导致两个账户的金额不同步,造成错误。

为了解决上述的问题,就需要通过数据的事务来完成,我们只需要在业务逻辑执行之前开启事务,执行完毕后提交事务。如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。


2,事务四大特性

2.1,原子性

原子性是指一个事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功,才算整个事务执行成功。

如果事务中有任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。

简而言之,原子性指的是:事务是不可分割的最小操作单元,事务中的操作执行要么全部成功,要么全部失败。


2.2,一致性

一致性是指在进行事务处理时,无论是执行成功还是失败,都要保证数据库系统处于一致的状态,保证数据库系统不会返回到一个未处理

的事务中。mysql中的一致性主要是由日志机制来实现的,通过日志记录数据库的所有变化,为事务恢复提供了跟踪记录。

简而言之,一致性指的是:事务完成时,必须使所有的数据都保持一致的状态。


2.3,隔离性

隔离性是指当一个事务在执行时,不会受到其他事务的影响。保证了未完成事务的所有操作与数据库系统的隔离,直到事务完成为止,才能看到事务的执行结果。隔离性相关的技术有并发控制、可串行化、锁等。

当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

简而言之,隔离性指的是:数据库系统提供的隔离机制,保证了事务不受外部并发操作的影响,而在独立环境下运行。


2.4,持久性

持久性是指事务一旦提交,其对数据库的修改就是永久性的。但需要注意的是,事务的持久性不能做到百分百的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所提交的数据可能都会丢失。

简而言之,持久性指的是:事务一旦提交或者回滚,它对数据库中的数据的改变就是永久的。


3,事务基本操作

3.1,开启事务

默认情况下,MySQL的提交方式是自动提交模式,也就是说用户执行的每一条SQL语句都会被当成一个单独的事务自动提交。如果不想执行的每一条SQL语句自动提交,那就需要显性地开启一个事务。

START TRANSACTION;

3.2,回滚事务

在开启事务后,如果事务中的某些SQL语句出现异常情况时,就不能提交当前的事务,这时就需要使用ROLLBACK进行回滚事务操作。

ROLLBACK;

需要注意的是,ROLLBACK只能针对并未提交的事务进行回滚,已提交的事务无法进行回滚。当执行完ROLLBACK 或者 COMMIT后,当前事务才会自动结束。


3.3,提交事务

在开启事务后,每条SQL语句不再自动提交,用户需要使用COMMIT来手动进行提交,只有事务提交后,事务中的操作才能生效。

COMMIT;

3.4,事务的保存点

在回滚事务时,事务内所有的操作都会被撤销。如果我们希望只撤销一部分的数据,可以使用保存点来实现。

SAVEPOINT 保存点名;

在设置保存点后,使用以下命令可以完成将事务回滚到指定保存点。

ROLLBACK TO SAVEPOINT 保存点名;

如果使用时不再需要保存点,可以使用以下指令完成删除保存点操作。

RELEASE SAVEPOINT 保存点名;

值得一提的是,一个事务中可以创建多个保存点,在提交事务后,事务中的保存点就会被删除。

另外,在回滚到某个保存点后,该该保存点后创建过的保存点也都会消失。


3.5,事务自动提交

默认情况下,MySQL的提交方式是自动提交模式,也就是说用户执行的每一条SQL语句都会被当成一个单独的事务自动提交。

如果不想执行的每一条SQL语句自动提交,有两种操作方法。第1种,可以通过将该组的SQL语句作为一个事务;第2种,可以通过更改AUTOCOMMIT变量的值来实现,将其值设为1,表示开启自动提交,设为0,表示关闭自动提交。

若想要查看当前会话的AUTOCOMMIT值,使用以下语法:

SELECT @@autocommit;

若想要关闭当前会话的事务自动提交,使用以下语法:

SET AUTOCOMMIT = 0;

上述语法执行完后,当前会话的事务就不会自动提交了,需要我们手动进行提交操作,才会提交事务。

若直接终止MySQL会话,MySQL会对当前会话进行回滚操作。


3.6,事务访问模式

默认情况下,事务的访问模式为READ WRITE(读写模式),表示事务可以执行读(查询)或写(更改,插入,删除等)操作。

如果开发需要,可以将事务的访问模式设置为READ ONLY(只读模式),禁止对表进行更改操作。其更改语法:

SET [SESSION | GLOBAL] TRANSACTION READ ONLY;

如果想将事务的访问模式由只读模式恢复成读写模式,可使用以下语法:

SET [SESSION | GLOBAL] TRANSACTION READ WRITE;

3.7,事务操作注意

1,事务不允许嵌套,如果在执行START TRANSACTION语句前上一个事务还未提交,会隐式地执行提交事务的操作。

2,事务的处理主要是针对数据表中数据的处理,不包括创建或删除数据库,数据表,以及修改表结构等操作,而且执行这些操作时会隐式提交事务。

3,MySQL5.7默认的存储引擎为InnoDB,该引擎支持事务,而另外一个常见的存储引擎MyISAM却不支持事务。对于MyISAM存储引擎的数据表,无论事务是否提交,对数据的操作都会立即失效,不会回滚。

4,在MySQL中,还可以使用BEGIN来显式地开启一个事务,但由于BEGINBEGIN...END冲突,故不推荐使用BEIGN来开启事务。


4,隔离级别操作

数据库是一个多用户的共享资源,MySQL允许多线程并发访问,因此用户可以通过不同的线程来执行不同的事务,为了保证这些事务之间不相互影响,因此对事务设置隔离级。

4.1,查看隔离级别

对于事务的隔离级别查看,MySQL提供了以下几种不同的方式,具体使用哪种方式进行查询需根据具体需求进行选择。

#查看全局隔离级别
SELECT @@global.transaction_isolation;

#查看当前会话中的隔离级别
SELECT @@session.transaction_isolation;
  
#查看下一个事务的隔离级别
SELECT @@transaction_isolation;

全局的隔离级影响的是所有连接MySQL的用户;当前会话的隔离级别则只是影响当前正在登录MySQL服务器的用户,不会影响其他用户;而下一个事务的隔离级别仅对当前用户的下一个事务操作有影响。

在默认情况下,上述三种方式的返回结果都是REPEATABLE READ,表示隔离级别为可重复读。


4.2,修改隔离级别

事务的隔离级别可以通过SET语句来进行修改事务的隔离级别。

#修改全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVER 参数值;

#修改当前会话中的隔离级别
SET SESSION TRANSACTION ISOLATION LEVER 参数值;
  
#修改下一个事务的隔离级别
SET TRANSACTION ISOLATION LEVER 参数值;

SET后的GLOBAL表示全局,SESSION表示当前会话,TRANSCATION表示事务,ISOLATION 表示隔离,LEVER表示级别。

参数值可以为READ UNCOMMITTED(读取未提交) READ COMMITTED(读取提交) REPEATABLE READ(可重复读) SERIALIZABLE(可串行化)


5,事务并发问题

5.1,脏读

脏读就是指一个事务读取了另外一个事务还没有提交的数据。

脏读的流程示意图:

脏读的具体操作实例:


5.2,不可重复读

不可重复读是指在一个事务中多次读取同一条记录,但先后读取到的数据却不同。

不可重复读的流程示意图:

不可重复读的具体操作实例:


5.3,幻读

幻读是指在一个事务内先后两次查询中的数据记录数不一致。

按照特定条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在。

幻读是由于其他事务做了插入记录的操作,进而导致记录数有所添加。

幻读的流程示意图:

幻读的具体操作实例:


6,事务隔离级别

隔离级别脏读不可重复读幻读
Read uncommitted✔️✔️✔️
Read committed✔️✔️
Repeatable Read(默认)✔️
Serializable

6.1,READ UNCOMMITTED

READ UNCOMMITTED表示读取未提交,是事务中最低的级别,在该级别下的事务可以读取到其它事务中未提交的数据,会出现脏读问题。

事务A和事务B的隔离级别为READ UNCOMMITTED,当事务A完成开启事务,和进行余额查询并对余额进行减少1000操作,但未提交事务,接着事务B在开启事务,进行查询余额操作,发现余额已经减少了1000,查询到的是事务A中修改后的余额数据,这种就是脏读。

为了避免脏读情况的发生,可将客户端的事务隔离级设为READ COMMITTED(或更高级别的隔离级)来避免此类情况。

首先将事务B的隔离级修改为READ COMMITTED(或更高级别的隔离级),再按上述操作正常进行,发现事务B不会在读取到事务A未提交的数据记录了,从而不会出现脏读的情况了。

注意:为了保证数据的一致性,该隔离级别在实际应用中几乎不使用。


6.2,READ COMMITTED

READ COMMITTED表示读取提交,是大多数DBMS(如SQL,Server,Oracle)的默认隔离级,但不包括MySQL。

在该隔离级别下,只能读取其它事务已经提交的数据,避免了脏读数据的情况,但这种情况下会出现不可重复读的问题。

事务A和事务B的隔离级别均为READ COMMITTED,当事务A完成开启事务和查询余额操作后,事务B就依次完成开启事务,查询余额,修改余额,提交事务等操作,紧接着在事务A中继续查询余额操作,发现在事务A中的先后两次的查询结果不同,这就是不可重复读问题。

为了避免不可重复读情况的发生,可将客户端的事务隔离级设为REPEATABLE READ(或更高级别的隔离级)来避免此类情况。

首先将事务B的隔离级修改为REPEATABLE READ(或更高级别的隔离级),再按上述操作正常进行,发现事务A先后两次查询结果是相同的,说明REPEATABLE READ可以避免不可重复读的情况,从而不会出现不可重复读的情况了。


6.3,REPEATABLE READ

REPEATABLE READ表示可重复读,是MySQL的默认事务隔离级,他解决了脏读问题和不可重复读问题。

其确保了同一事务的多个实例在并发读取数据时,会看到相同的结果,但在理论上会出现幻读的并发问题。

幻读和不可重复读有些类似,同样发生在两次查询过程中,不同的是,幻读是由于其他事务添加数据导致记录数有所增加。

MySQL的InnoDB存储引擎通过多版本并发控制机制解决了幻读的并发问题。

事务B的隔离级别为REPEATABLE READ,当客户端完成开启事务B,添加数据和提交事务的操作后,接着在另外一个客户端完成开启事务A,查询数据记录,发现无id为3的数据,但在插入id为3的数据时,发现无法插入,但查询时没有此数据,这就是幻读的情况。


为了避免幻读情况的发生,可将客户端的事务隔离级设为SERIALIZABLE 来避免此类情况。


6.4,SERIALIZABLE

SERIALIZABLE表示可串行化,是MySQL中隔离级的最高级别,它在每个读取的数据行上加锁,使之不会发生冲突,从而解决了脏读、不可重复读和幻读的问题。

由于加锁可能导致超时和锁竞争现象,性能是4种隔离级中最低的。除非为了数据的稳定性,需要强制减少并发的情况时,才会选择此种隔离级。

事务B的隔离级别为SERIALIZABLE,当事务B完成开启事务和查询余额操作后,再让事务A进行开启事务和修改余额操作,会发现修改操作一直在等待,而不是立即就执行成功,这是因为事务B还未进行提交事务操作,等待事务B进行完提交事务操作。

如果在事务A的更新操作等待时,提交事务B,事务A中的更新操作才会被执行,并提示执行结果:

如果事务B一直未进行提交事务的操作,事务A中的更新操作就会一直等待,直至超时后,出现如下提示:锁等待超时,尝试重启事务。

在默认情况下,锁等待的超时时间是50秒,可以通过以下指令来查询:

SELECT @@innodb_lock_wait_timeout;


结语

这就是本期博客的全部内容啦,想必大家已经对MySQL进阶中的事务的相关知识有了全新地认识和理解吧,如果有什么其他的问题无法自己解决,可以在评论区留言哦!

最后,如果你觉得这篇文章写的还不错的话或者有所收获的话,麻烦小伙伴们动动你们的小手,给个三连呗(点赞👍,评论✍,收藏📖),多多支持一下!各位的支持是我最大的动力,后期不断更新优质的内容来帮助大家,一起进步。那我们下期见!


以上是关于MySQL进阶MySQL事务详解的主要内容,如果未能解决你的问题,请参考以下文章

MySQL事务隔离级别锁相关的详解

Python进阶篇:MySQL隔离级别详解

Python进阶篇:MySQL隔离级别详解

Python进阶篇:MySQL隔离级别详解

[MySQL]事务ACID详解

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)