序号 | 系列文章 |
---|---|
5 | 【MySQL基础】字符集与校对集详解 |
6 | 【MySQL基础】MySQL单表操作详解 |
7 | 【MySQL基础】运算符及相关函数详解 |
8 | 【MySQL基础】MySQL多表操作详解 |
大家好,我是小杨!前面我为大家介绍完了MySQL中的基础知识,那么从今天开始我将为大家介绍MySQL中的进阶知识,那么今天我们就从MySQL中的事务这部分内容开始说起,希望大家能够收获多多!
事务就是针对数据库的一组操作,它是由一条或多条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块钱, 而李四的金额没变, 这就会导致两个账户的金额不同步,造成错误。
为了解决上述的问题,就需要通过数据的事务来完成,我们只需要在业务逻辑执行之前开启事务,执行完毕后提交事务。如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。
原子性是指一个事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功,才算整个事务执行成功。
如果事务中有任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。
简而言之,原子性指的是:事务是不可分割的最小操作单元,事务中的操作执行要么全部成功,要么全部失败。
一致性是指在进行事务处理时,无论是执行成功还是失败,都要保证数据库系统处于一致的状态,保证数据库系统不会返回到一个未处理
的事务中。MySQL中的一致性主要是由日志机制来实现的,通过日志记录数据库的所有变化,为事务恢复提供了跟踪记录。
简而言之,一致性指的是:事务完成时,必须使所有的数据都保持一致的状态。
隔离性是指当一个事务在执行时,不会受到其他事务的影响。保证了未完成事务的所有操作与数据库系统的隔离,直到事务完成为止,才能看到事务的执行结果。隔离性相关的技术有并发控制、可串行化、锁等。
当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
简而言之,隔离性指的是:数据库系统提供的隔离机制,保证了事务不受外部并发操作的影响,而在独立环境下运行。
持久性是指事务一旦提交,其对数据库的修改就是永久性的。但需要注意的是,事务的持久性不能做到百分百的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所提交的数据可能都会丢失。
简而言之,持久性指的是:事务一旦提交或者回滚,它对数据库中的数据的改变就是永久的。
默认情况下,MySQL的提交方式是自动提交模式,也就是说用户执行的每一条SQL语句都会被当成一个单独的事务自动提交。如果不想执行的每一条SQL语句自动提交,那就需要显性地开启一个事务。
START TRANSACTION;
在开启事务后,如果事务中的某些SQL语句出现异常情况时,就不能提交当前的事务,这时就需要使用ROLLBACK
进行回滚事务操作。
ROLLBACK;
需要注意的是,ROLLBACK
只能针对并未提交的事务进行回滚,已提交的事务无法进行回滚。当执行完ROLLBACK
或者 COMMIT
后,当前事务才会自动结束。
在开启事务后,每条SQL语句不再自动提交,用户需要使用COMMIT
来手动进行提交,只有事务提交后,事务中的操作才能生效。
COMMIT;
在回滚事务时,事务内所有的操作都会被撤销。如果我们希望只撤销一部分的数据,可以使用保存点来实现。
SAVEPOINT 保存点名;
在设置保存点后,使用以下命令可以完成将事务回滚到指定保存点。
ROLLBACK TO SAVEPOINT 保存点名;
如果使用时不再需要保存点,可以使用以下指令完成删除保存点操作。
RELEASE SAVEPOINT 保存点名;
值得一提的是,一个事务中可以创建多个保存点,在提交事务后,事务中的保存点就会被删除。
另外,在回滚到某个保存点后,该该保存点后创建过的保存点也都会消失。
默认情况下,MySQL的提交方式是自动提交模式,也就是说用户执行的每一条SQL语句都会被当成一个单独的事务自动提交。
如果不想执行的每一条SQL语句自动提交,有两种操作方法。第1种,可以通过将该组的SQL语句作为一个事务;第2种,可以通过更改AUTOCOMMIT
变量的值来实现,将其值设为1,表示开启自动提交,设为0,表示关闭自动提交。
若想要查看当前会话的AUTOCOMMIT值,使用以下语法:
SELECT @@autocommit;
若想要关闭当前会话的事务自动提交,使用以下语法:
SET AUTOCOMMIT = 0;
上述语法执行完后,当前会话的事务就不会自动提交了,需要我们手动进行提交操作,才会提交事务。
若直接终止MySQL会话,MySQL会对当前会话进行回滚操作。
默认情况下,事务的访问模式为READ WRITE
(读写模式),表示事务可以执行读(查询)或写(更改,插入,删除等)操作。
如果开发需要,可以将事务的访问模式设置为READ ONLY
(只读模式),禁止对表进行更改操作。其更改语法:
SET [SESSION | GLOBAL] TRANSACTION READ ONLY;
如果想将事务的访问模式由只读模式恢复成读写模式,可使用以下语法:
SET [SESSION | GLOBAL] TRANSACTION READ WRITE;
1,事务不允许嵌套,如果在执行START TRANSACTION
语句前上一个事务还未提交,会隐式地执行提交事务的操作。
2,事务的处理主要是针对数据表中数据的处理,不包括创建或删除数据库,数据表,以及修改表结构等操作,而且执行这些操作时会隐式提交事务。
3,MySQL5.7默认的存储引擎为InnoDB,该引擎支持事务,而另外一个常见的存储引擎MyISAM却不支持事务。对于MyISAM存储引擎的数据表,无论事务是否提交,对数据的操作都会立即失效,不会回滚。
4,在MySQL中,还可以使用BEGIN
来显式地开启一个事务,但由于BEGIN
与BEGIN...END
冲突,故不推荐使用BEIGN
来开启事务。
数据库是一个多用户的共享资源,MySQL允许多线程并发访问,因此用户可以通过不同的线程来执行不同的事务,为了保证这些事务之间不相互影响,因此对事务设置隔离级。
对于事务的隔离级别查看,MySQL提供了以下几种不同的方式,具体使用哪种方式进行查询需根据具体需求进行选择。
#查看全局隔离级别
SELECT @@global.transaction_isolation;
#查看当前会话中的隔离级别
SELECT @@session.transaction_isolation;
#查看下一个事务的隔离级别
SELECT @@transaction_isolation;
全局的隔离级影响的是所有连接MySQL的用户;当前会话的隔离级别则只是影响当前正在登录MySQL服务器的用户,不会影响其他用户;而下一个事务的隔离级别仅对当前用户的下一个事务操作有影响。
在默认情况下,上述三种方式的返回结果都是REPEATABLE READ
,表示隔离级别为可重复读。
事务的隔离级别可以通过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(可串行化)
。
脏读就是指一个事务读取了另外一个事务还没有提交的数据。
脏读的流程示意图:
脏读的具体操作实例:
不可重复读是指在一个事务中多次读取同一条记录,但先后读取到的数据却不同。
不可重复读的流程示意图:
不可重复读的具体操作实例:
幻读是指在一个事务内先后两次查询中的数据记录数不一致。
按照特定条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在。
幻读是由于其他事务做了插入记录的操作,进而导致记录数有所添加。
幻读的流程示意图:
幻读的具体操作实例:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | ✔️ | ✔️ | ✔️ |
Read committed | ❌ | ✔️ | ✔️ |
Repeatable Read(默认) | ❌ | ❌ | ✔️ |
Serializable | ❌ | ❌ | ❌ |
READ UNCOMMITTED
表示读取未提交,是事务中最低的级别,在该级别下的事务可以读取到其它事务中未提交的数据,会出现脏读问题。
事务A和事务B的隔离级别为READ UNCOMMITTED,当事务A完成开启事务,和进行余额查询并对余额进行减少1000操作,但未提交事务,接着事务B在开启事务,进行查询余额操作,发现余额已经减少了1000,查询到的是事务A中修改后的余额数据,这种就是脏读。
为了避免脏读情况的发生,可将客户端的事务隔离级设为READ COMMITTED
(或更高级别的隔离级)来避免此类情况。
首先将事务B的隔离级修改为READ COMMITTED
(或更高级别的隔离级),再按上述操作正常进行,发现事务B不会在读取到事务A未提交的数据记录了,从而不会出现脏读的情况了。
注意:为了保证数据的一致性,该隔离级别在实际应用中几乎不使用。
READ COMMITTED
表示读取提交,是大多数DBMS(如SQL,Server,Oracle)的默认隔离级,但不包括MySQL。
在该隔离级别下,只能读取其它事务已经提交的数据,避免了脏读数据的情况,但这种情况下会出现不可重复读的问题。
事务A和事务B的隔离级别均为READ COMMITTED
,当事务A完成开启事务和查询余额操作后,事务B就依次完成开启事务,查询余额,修改余额,提交事务等操作,紧接着在事务A中继续查询余额操作,发现在事务A中的先后两次的查询结果不同,这就是不可重复读问题。
为了避免不可重复读情况的发生,可将客户端的事务隔离级设为REPEATABLE READ
(或更高级别的隔离级)来避免此类情况。
首先将事务B的隔离级修改为REPEATABLE READ
(或更高级别的隔离级),再按上述操作正常进行,发现事务A先后两次查询结果是相同的,说明REPEATABLE READ
可以避免不可重复读的情况,从而不会出现不可重复读的情况了。
REPEATABLE READ
表示可重复读,是MySQL的默认事务隔离级,他解决了脏读问题和不可重复读问题。
其确保了同一事务的多个实例在并发读取数据时,会看到相同的结果,但在理论上会出现幻读的并发问题。
幻读和不可重复读有些类似,同样发生在两次查询过程中,不同的是,幻读是由于其他事务添加数据导致记录数有所增加。
MySQL的InnoDB存储引擎通过多版本并发控制机制解决了幻读的并发问题。
事务B的隔离级别为REPEATABLE READ
,当客户端完成开启事务B,添加数据和提交事务的操作后,接着在另外一个客户端完成开启事务A,查询数据记录,发现无id为3的数据,但在插入id为3的数据时,发现无法插入,但查询时没有此数据,这就是幻读的情况。
为了避免幻读情况的发生,可将客户端的事务隔离级设为SERIALIZABLE
来避免此类情况。
SERIALIZABLE
表示可串行化,是MySQL中隔离级的最高级别,它在每个读取的数据行上加锁,使之不会发生冲突,从而解决了脏读、不可重复读和幻读的问题。
由于加锁可能导致超时和锁竞争现象,性能是4种隔离级中最低的。除非为了数据的稳定性,需要强制减少并发的情况时,才会选择此种隔离级。
事务B的隔离级别为SERIALIZABLE
,当事务B完成开启事务和查询余额操作后,再让事务A进行开启事务和修改余额操作,会发现修改操作一直在等待,而不是立即就执行成功,这是因为事务B还未进行提交事务操作,等待事务B进行完提交事务操作。
如果在事务A的更新操作等待时,提交事务B,事务A中的更新操作才会被执行,并提示执行结果:
如果事务B一直未进行提交事务的操作,事务A中的更新操作就会一直等待,直至超时后,出现如下提示:锁等待超时,尝试重启事务。
在默认情况下,锁等待的超时时间是50秒,可以通过以下指令来查询:
SELECT @@innodb_lock_wait_timeout;
这就是本期博客的全部内容啦,想必大家已经对MySQL进阶中的事务的相关知识有了全新地认识和理解吧,如果有什么其他的问题无法自己解决,可以在评论区留言哦!
最后,如果你觉得这篇文章写的还不错的话或者有所收获的话,麻烦小伙伴们动动你们的小手,给个三连呗(点赞,评论✍,收藏),多多支持一下!各位的支持是我最大的动力,后期不断更新优质的内容来帮助大家,一起进步。那我们下期见!