文章目录
MySQL事务
一、事务介绍
什么是事务
一个事务是一个完整的业务逻辑单元,不可再分。
比如:银行账户转账,从A账户向B账户转账10000.需要执行两条update语句:
update t_act set balance = balance - 10000 where actno = ‘act-001’;
update t_act set balance = balance + 10000 where actno = ‘act-002’;以上两条DML语句必须同时成功,或者同时失败,不允许出现一条成功,一条失败。
要想保证以上的两条DML语句同时成功或者同时失败,那么就需要使用数据库的“事务机制”。
应用场景
事物的和事务相关的语句只有:DML
语句。(insert delete update)—— 事务之和对数据产生变化的操作有关
为什么?因为它们这三个语句都是和数据库表当中的“数据”相关的。
事务的存在是为了保证数据的完整性,安全性。
如果只有一条DML语句,还需要事务机制嘛?
不需要!
但实际情况不是这样的,通常一个“事儿(事务【业务】)”需要多条DML语句共同联合完成。
综上所述:事务机制就是在处理某一个业务(事务)时多条DML
语句同时成功或者同时失败,保证数据的完整性,安全性
事务如何实现
事务如何做到多条DML语句同时成功或者同时失败?
MySQL中的InnoDB存储引擎:提供了一组用来记录事务活动的日志文件(log)
事务开启了!
insert
insert
update
delete
…
事务结束了!
在事务执行的1过程中,每一条DML
语句都会记录到事务性活动的日志文件
中,在事务的执行过程中,我们可以提交事务,也可以回滚事务。
提交事务?
提交事务,就会清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。
提交事务标志着,事务的结束!并且是一种全部成功(所有DML全部成功)的结束
事务回滚?
将之前的所有
DML
全部撤销,并且清空事务性活动的日志文件。回滚事务,标志着事物的结束!并且是一种全部失败(所有DML全部失败)的结束
怎么提交和回滚事务
- 事务提交:commit
- 事务回滚:rollback ——(回滚到上一次
commit
的地方)
测试:MySQL中的事务默认提交方式是怎么样的?手动提交还是自动提交呢?——自动提交。
自动提交?—— 没执行一条DML
语句,就会提交一次事务
上述我们删除表中的数据后,进行回滚,并不能将原来的数据恢复,那是因为,在执行delete
语句后,事务自动提交(默认),然后就会清空事务性活动的日志文件,当进行回滚时无法从日志文件中拿到并恢复数据!
这种自动提交实际是不符合我们的开发习惯的,因为一个业务通常需要多条
DML
语句共同执行完成的,为了保证数据的安全,必须要求同时成功完成后再进行提交,所以不能执行一条就提交一条!
我们如何关闭MySQL的默认自动提交事务,而是自己手动提交事务呢?
关闭自动提交事务命令(我们自己开启事务),先执行这一条命令:
start transaction
然后就是DML语句
最后提交事务
事务回滚:
提交事务与回滚事务:滚不回去了!
二、事务的特性
一个事务就是完成一件事(一个完整的业务逻辑)
事务有四大特性:ACID
- A:原子性:事务是最小的工作单元,不可再分。
- C:一致性:事务必须保证
多条DML语句
同时成功或者同时失败。 - I:隔离性:事务A与事务B之间具有隔离。
- D:持久性:持久性说的是最终数据必须持久化到硬盘文件中,事务才算成功的结束。(真正存到数据库中)
三、事务的隔离性即演示
1. 隔离性的4个级别
事务隔离性存在隔离级别,理论上隔离级别包括4个:
- 第一级别:读未提交(
read uncommitted
)
对方事务还没有提交,我们当前事务可以读取到对方未提交的数据。
读未提交存在脏读(Dirty Read)现象:表示读到了脏的数据。 - 第二级别:读已提交(read committed)
对方事务提交之后的数据我方可以读取到。
这种隔离级别解决了: 脏读现象没有了。
读已提交存在的问题是:不可重复读。 - 第三级别:可重复读(repeatable read)————(MySQL默认的事务隔离级别)
这种隔离级别解决了:不可重复读问题。
这种隔离级别存在的问题是:读取到的数据是幻象。 - 第四级别:序列化读/串行化读(serializable)
解决了所有问题。
效率低。需要事务排队。
MySQL命令行的默认配置中事务都是自动提交的,即执行SQL语句就会马上执行COMMIT操作。可以用命令START TRANSACTION
开始一个事务。
我们可以通过下面命令设置事务隔离级别。
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
第1级别:读未提交
set global transaction isolation level read uncommitted;
第2级别:读已提交
set global transaction isolation level read committed;
第3级别:可重复读
set global transaction isolation level repeatable read;
cd C:\Program Files\MySQL\MySQL Server 8.0\bin
查看当前事务级别:
select @@transaction_isolation;
我们再来看一下我们在实际操作中使用到的一些并发控制语句:
- START TRANSACTION | BEGIN :显示的开启一个事务。
- COMMIT:提交事务,使得对数据库做的所有修改成为永久性。
- ROLLBACK:回滚到结束用户的事务,并撤销正在进行的所有未提交的修改。
2.隔离性演示
(1)第一级别
第一级别:读未提交(
read uncommitted
)
对方事务还没有提交,我们当前事务可以读取到对方未提交的数据。
读未提交存在脏读(Dirty Read)现象:表示读到了脏的数据。
事务A--------------------------------------事务B
窗口A和窗口B并行进行测试检验!
1、在窗口A将全局事务级别设为 read uncommitted
set global transaction isolation level read uncommitted;
成功,退出,再进入
查看级别等级——设置成功
select @@transaction_isolation;
2、同时开两个命令窗口,登录数据库,进入到我们测试用的数据库
窗口A:
1、开启事务
start transaction;
2、查一下数据
select * from user;
# 表此时为空
窗口B:
1、开启事务
start transaction;
2、插入数据(未提交
(事务未提交,没有commit))
insert into user values("张三");
再回到窗口A:查看数据,发现数据查出来了!!!
第一级别:
第一级别:读未提交(
read uncommitted
)
对方事务还没有提交,我们当前事务可以读取到对方未提交的数据。
(2)第二级别
事务A--------------------------------------事务B
窗口A和窗口B并行进行测试检验!
1、在窗口A将全局事务级别设为 read committed
set global transaction isolation level read committed;
成功,退出,再进入
查看级别等级——设置成功
select @@transaction_isolation;
2、同时开两个命令窗口,登录数据库,进入到我们测试用的数据库
窗口A:
1、开启事务
start transaction;
2、查一下数据
select * from user;
# 表此时为空
窗口B:
1、开启事务
start transaction;
2、插入数据(未提交
(事务未提交,没有commit))
insert into user values("李四");
3、再回到窗口A:查看数据,数据未查出来!!!
4、回到窗口B,进行事务提交
commit;
5、再回到窗口A:查看数据,数据查出来了!!!
第二级别:
1第二级别:读已提交(read committed)
对方事务提交之后的数据我方可以读取到。
这种隔离级别解决了: 脏读现象没有了。
读已提交存在的问题是:不可重复读。
(3)第三级别
事务A--------------------------------------事务B
窗口A和窗口B并行进行测试检验!
1、在窗口A将全局事务级别设为 repeatable read
set global transaction isolation level repeatable read;
成功,退出,再登录进入
查看级别等级——设置成功
select @@transaction_isolation;
2、同时开两个命令窗口,登录数据库,进入到我们测试用的数据库
窗口A:
我们事先先插入一条数据:
insert into user values("6666");
1、开启事务
start transaction;
2、查一下数据
select * from user;
# 表此时有一条数据6666
窗口B:
1、开启事务
start transaction;
2、插入数据(未提交
(事务未提交,没有commit))
insert into user values("李四");
insert into user values("李四");
insert into user values("李四");
4、提交事务
commit;
窗口A:
5、再回到窗口A:查看数据,数据未查出来!!!
6、窗口A中,提交事务后,就可以看到新数据了
1第三级别:可重复读(repeatable read)
这种隔离级别解决了:不可重复读问题。
这种隔离级别存在的问题是:读取到的数据是幻象。
总结:两个事务是独立的,当对同一张表同时开启事务,事务B中进行DML
语句,commit
结束事务B
后,只要事务A
此时还没结束,那么A事务中查询到的数据都是事务B提交前的数据(两个事务同时进行时,事务独立开来了,互不影响的(第三级别)),只有当事务A
也commit
后,事务B
中的DML
语句产生的影响才会作用到事务A
,此时查询到的数据是更新后的!
(4)第四级别
窗口A和窗口B并行进行测试检验!
1、在窗口A将全局事务级别设为 serializable
set global transaction isolation level serializable;
成功,退出,再进入
查看级别等级——设置成功
select @@transaction_isolation;
2、同时开两个命令窗口,登录数据库,进入到我们测试用的数据库
窗口A:
1、开启事务
start transaction;
2、查一下数据
select * from user;
# 表此时为空
3、插入数据(未提交
(事务未提交,没有commit)—— 事务A没结束)
insert into user values("张三");
窗口B:
1、开启事务(事务要同时开启的)
start transaction;
2、查询数据,发现窗口B,光标卡住了
因为两个事务同时操作这张表了,第四级别四需要排队的!!!
窗口A:
再回到窗口A:结束事务——发现窗口B立即查出数据来了(说明第四级别事务需要排队!!!!)