目录
注:本文的绿色背景文字为关键操作
黄色背景为重要概念
事务(Transaction)是由一系列对系统中数据进⾏访问与更新的操作所组成的⼀个程序执行逻辑单元。
一、 事务的语法
1、事务的启动:start transaction / begin;
2、事务的提交:commit;
3、事务的回滚(撤销操作):rollback;
注意:事务的测试,要求当前数据库的引擎为InnoDB,其他类型不支持事务
我们在以前的操作中,其实每一步执行,都是自动提交了一个事务,比如:
我们现在有一个年级的数据表:我们现在insert into grade VALUES(5,'aa');
表中添加了一个aa,我们在命令行中查询:也是有aa的。这是因为,系统自动帮你提交了事务。
那如果我们现在开启一个事务:添加bb,在表中也是显示的
但是在命令行中查询,发现:并没有。这是因为事务,并没有提交(commit)代表你不想让数据存入数据库。
那我们浅提交一下吧!
现在在命令行中再次查询:就有了bb。
如果我们在进行完添加数据后,不想添加了怎么办?
我们使用回滚:rollback
这样就又撤回了上一步操作,在命令行中查询也是没有上一步添加的数据的。
注意:使用回滚必须要在提交之前,提交后使用无效!!~~
二、 事务的特性ACID (面试易遇)
A:原⼦性(Atomicity)
事务的原⼦性是指事务必须是⼀个原子的操作序列单元。事务中包含的各项操作在⼀次执⾏过程中,只允许出现两种状态之一。
(1)全部执行成功
(2)全部执行失败
比如:我们在一个事务中添加了两个数据,那么结果只有两种,要么全部添加成功,要么全部添加失败,不可能一个成功一个失败!
C: ⼀致性(Consistency)
事务的一致性是指事务的执⾏不能破坏数据库数据的完整性和一致性,一个事务在执⾏之前和执行之后,数据库都必须处以⼀致性状态。其实一致性的大白话就是:如果把数据库看成一个大仓库,那么数据库在操作前和操作后里面的的内容总量不能变。比如数据库有1000块钱分别a1000,b0。那么a就不能转给b2000,a变成-1000.,这是违背客观真理的,数据库中的内容可以在库中转换,但总量要保持一致。这就是数据库的一致性。
I: 隔离性(Isolation)
事务的隔离,是指在并发环境中,并发的事务是互相隔离的。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。比如一千个人同时向外转账,他们的转账路径是不会交汇的,是彼此隔离的。
D:持久性(Duration)
事务的持久性是指事务⼀旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态。
三、 事务并发问题
脏读:读取到了没有提交的数据, 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。同一个事务中,数据前后不一致。
不可重复读:同⼀条命令返回不同的结果集(更新).事务 A 多次读取同一数据,事务 B 在事务A 多次读取的过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果不一致。
幻读:重复查询的过程中,数据发⽣了量的变化(insert, delete)。同一个事务中,数据前后不一致。
四、 事务隔离级别
事务的隔离级别用来解决上一点说到的事务的并发问题。
从上到下,级别越来越高,读未提交可能发生脏读、不可重复读、幻读。但是顺序读,不可能发生任何一种。
但是随着隔离级别的增高,安全性虽然越高,但并发性也越来越差。所以,虽然理论上我们可以设置为顺序读,但是在并发量大的情况下,还是不允许使用顺序读的。一般数据库的默认级别是:读已提交或可重复读。
查看当前会话中事务的隔离级别:
mysql8之前的命令:select @@tx_isolation;
mysql8之后的命令:select @@transaction_isolation;
可以看到我的是“可重复读”,它可以避免脏读和不可重复读,可能出现幻读。
设置当前会话中事务的隔离级别:set session transaction isolation level 隔离级别。
注意:要在数据库和命令行中都设置!
---------------------------------------------------------------------------------------------------------------------------------
脏读:
现在,我们的数据库的隔离级别为“读未提交”,我来给大家演示一下脏读:
先建立一个存钱表:
现我们开启两个事务:
1、开启事务1:
2、开启事务2:
3、事务2:在命令行中修改取钱(修改余额):
4、在事务1中查询余额:
5、注意:此时,事务1得到id为1的余额为0。但是如果此时在事务2中撤销刚刚的操作:
6、 但是事务1并不知道,事务1中id为1的money还是0,事务1继续向里面存钱500,此时,应该得到的数据为money=500,但是:
这就是出现了脏读的问题。
那么我们为了避免这种脏读的问题出现,我们应该将隔离级别调高,我们将其调为读已提交 (read committed)
这样就算我们在事务2中修改了money;
我们事务1中的money也不会变:前后始终保持一致!避免脏读!
直到事务1提交数据后,两方的数据才得到统一:
但是!读已提交只能避免脏读,无法避免“不可重复读”!
不可重复读:
下面我来进行“不可重复读”的演示:
先开启两个事务(数据都还和上面的一样:2000,1000),在事务2中修改数据:
在事务1中读取:
事务1中两次读取的数据并不统一,这就是出现了可重复读的问题。在同一事务中多次查询结果都不一致!
所以我们还需要将隔离级别调高到“可重复读”级别(与上面调级别语法一样)
然后在事务2中修改id为1的money:改为2000;并且提交
此时,我们在事务1中查询,结果还是1000,保证了数据的一致。
只有在事务1执行了commit提交操作后,事务1才变为2000:
但是同样的,隔离级别“可重复读”又不能避免“幻读”的出现:
幻读:
(数据为上表:2000,0)
1、开启事务1、2
2、在事务2插入一条数据并提交:
3、在事务1中对数据进行修改,在这里事务1中本应是两条数据。
但是改完后,却发现是三条数据被进行了修改,这就是出现了“幻读”。
为了避免“幻读”,我们可以将隔离级别改为“顺序读”(不再演示)。
当隔离级别为“顺序读”时:
1、开启事务1,查询数据:(注意:必须查询数据,这样才是事务1先执行,事务2才不能执行)
2、开启事务2,添加数据,发现无法继续执行,这就是因为顺序读要求所有的事务排队执行。
那么只有在事务1commit提交后,事务2才可以继续执行。
注意这个error,就是因为刚刚排队等待时间太久了,所以出了bug,我们再次添加即可,并且事务1再次查询,也可以查询到了。
五、 不同隔离级别的锁的情况(了解)
不同的隔离级别其实就是对数据上不同程度的锁。
1. 读未提交(RU): 有行级的锁,没有间隙锁。它与RC的区别是能够查询到未提交的数据。
2. 读已提交(RC):有行级的锁,没有间隙锁,读不到没有提交的数据。
3. 可重复读(RR):有行级的锁,也有间隙锁,每次读取的数据都是一样的,并且没有幻读的情况。
4. 序列化(S):有行级锁,也有间隙锁,读表的时候,就已经上锁了
六、 隐式提交(了解)
DQL:查询语句句 DML:写操作(添加,删除,修改)
DDL:定义语句句(建库,建表,修改表,索引操作,存储过程,视图)
DCL:控制语⾔言(给⽤用户授权,或删除授权)
DDL(Data Define Language):都是隐式提交。
隐式提交:执行这种语句句相当于执⾏行行commit;
数据库默认都是隐式提交。