事务的特性:
隔离性:多个事务在并发执行的时候,多个事务执行的一个行为模式,当一个事务执行的时候,另一个事务执行的一个行为模式是什么?
1)A,原子性,一个事务中的所有操作,要么全部执行成功,要么全部执行失败,要么全部执行,要么全部不执行,不会结束在某一个中间环节,事务在执行的时候发生错误,会被回滚到事务执行之前的状态,就好像这个事务从来没有发生过一样
2)C,一致性,在事务开启和事务结束之后,对数据库的完整性没有发生破坏,转账前后,总钱数没有发生变化,张三的钱数和李四钱数在转账前后的总钱数是不会发生改变的;
3)I,持久性,事务处理结束之后,对数据的修改就是持久的,数据永久地被存下来,关机重启之后也不会丢失,系统故障也不会丢失;
4)D,隔离性,数据库允许多个并发事务同时对其数据进行读写和修改能力,隔离性可以防止多个事务并发由于交叉执行而导致数据的不一致,事务隔离分为四种级别,多个单个事务同时并发执行的时候,多个事务之间行为模式;
5)而事务的传播机制是指当一个程序调用链里面有多个方法的时候,多个方法进行相互调用的时候,多个方法又有自己的事务的时候,这就是嵌套事务,当程序中出现嵌套事务之后,嵌套事务的行为模式,Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递;
一个讲的是列,是事务传播机制
一个讲的是行,是事务隔离级别
6)为什么要设置事务的隔离级别?
设置事务的隔离级别是为了保证多个并发事务执行更可控,更符合操作者预期的,事务的隔离级别就是为了说为了防止其他的事务影响到当前事务执行的一种策略
7)用咱们的CMD连接数据库:mysql -uroot -p(数据库的密码) 数据库名,在客户端执行这个操作就可以进行连接数据库了
select @@global.tx_isolation,@@tx_isolation;查看当前隔离级别和系统默认隔离级别 @@global.tx_isolation这个就是系统默认的隔离级别,还有咱们的@@tx_isloaction是咱们的当前客户端的默认隔离级别
使用两个客户端来进行演示一下MYSQL的事务的四种隔离级别:
一:脏读问题(读未提交,就开始读):
这个对应MYSQL的隔离级别就是read uncommitted
1)表示读未提交,也叫做未提交读,该隔离级别的事务可以看到其他事务中没有提交的数据,而未提交的数据可能会发生回滚,我们就把一个事务读到别的事务没有提交的数据称之为脏数据,因为该事务隔离级别可以看到其他事务没有提交的数据,我们把该隔离级别读到的数据称之为脏数据,把这个问题称之为脏读;
2)这个是指一个事务B读取到了另一个事务A未提交保存的数据,此后这个A事务进行了回滚操作,从而导致了B事务读到了一条不存在的脏数据,此时B事务读到的就是一个中间态的数据,B事务就可能会使用这条脏数据,但是后来A事务直接将事务回滚,该数据就不存在了;
3)我们是可以使用两个MYSQL客户端来进行演示的:
此时我们先将客户端A的事务隔离级别设置成读未提交,而B的客户端的默认隔离级别是系统默认的隔离级别:
执行 客户端A 客户端B 1 set session transaction isolation level read uncommited 2 start transaction 3 start transaction 4 insert into city values("西安") 5 select * from city(此时我们读到一条数据,这个数据就是脏数据) 6 rollback 7 select * from city 啥也读不到 我们发现在我们的事务B新增数据进行完成回滚操作之后,我们的客户端A在进行我们此时在city表中是无法进行查询到任何数据的;
二)不可重复读问题(写完了读,读已提交):
但是从我的理解角度来说,同一个时间段不同的查询得到了不同的结果,但是读到的数据都是真实的在磁盘的上面进行修改了
2.1)这个时候一个事务不可以看到另一个事务未提交的数据了,但是可以看到其他事务提交的结果,A事务和B事务在执行的时候,B未提交的数据A是看不到的;
2.2)我们在同一个事务中,同一个查询在不同的时间段内得到了不同的结果,假设咱们的事务在T1的时候读取到了某一行数据,我们在T2时间内再次重新读取到这一行的时候,这一行的数据我们发现此时已经被修改了;
执行 客户端A 客户端B 1 set session transaction isolation level readcommited 2 3 select * from city(查询到的是西安的数据) 4 start transaction
5 执行SQL语句:
update city set name="长安" where name="西安“
6 commit 7 select * from city(此时我们读到的数据是长安) 我们从上述结果中我们发现,客户端A被设置成了读已提交的事务隔离级别之后,使用相同的SQL两次进行读取到的同一条数据,内容是不一样的,这就是不可重复读,主要是针对修改操作来说的,在事务1读取的过程中可以读到其他事务提交的结果;
总结:该隔离级别的事务能读到已经提交事务的数据,因此他不会出现脏读问题,但是在事务执行的过程中可以读到其他事务提交的结果,所以在不同的时间的相同的SQL查询的时候,可能会得到不同的结果;
三:幻读问题:同一个查询在不同时间内出现了不同的结果
1)这就是事务中的幻读问题,咱们举个例子来说,当我们开启事务之后,我们执行第一次select,我们进行查询出来了一条语句,就类似于幻觉;
2)但是当我们再次执行第二次Select查询之后,我们查询到了两行,第二次返回了第一次的·select查询没有进行返回的一行,这一行就类似于幻想行,下面我们举个例子来理解一下:
步骤 客户端A 客户端B 1 set session transaction isolaction level repeatable read 2 start transaction 3 select * from city where id<5 4 start transaction 5 insert into city city(id,name) values(2,北京); 6 commit 7 update city set name="京城” where id=2 8 select * from city where id<5
从上述执行流程来看,我们最后一条查询语句,查出了两条数据,客户端A一开始就设置了可重复读的事务隔离级别之后,使用相同的SQL却查询出来了不一样的结果,第一次查询出了一条数据,第二行却查询出了两条数据,多出来的这条数据就叫做幻象行,所以说在可重复读的情况下可能会出现幻读的问题;
最后总结:
1)脏读是读到了其他事务未提交的数据不可重复读和幻读是读到了其他事务提交修改的数据
2)但是幻读是读取到了其他事务新增或者删除的幻象行数据;
3)幻读和不可重读描述的侧重点是不一样的,幻读描述的是新增或者删除操作,不可重复读描述的是修改操作的,会强制事务进行排序;
咱们Spring的事务隔离级别:解决的是多个事务共同调用数据库的问题
我们需要进行事务的隔离级别只需要进行设置@Transaction里面的isolation属性即可
@Transactional(isolation = Isolation.DEFAULT,timeout =60)
1)Isolation.DEFAULT:以连接的数据库的事务隔离级别为主,使用链接的对应数据库的隔离级别,如果使用MYSQL,那么事务隔离级别就是read committed;
2)Isolation.READ_UNCOMMITTED:读未提交,可以读到未提交的事务,是存在脏读的;
3)Isolation.READ_COMMITTED:读已经提交,只能读取到已经提交的事务,解决了脏读,但是还是存在着不可重复读的问题;
4)Isolation.REPEATABLE_READ:可重复读,解决了不可重复读的问题,但存在着幻读,这是MYSQL的一个默认隔离级别;
5)Isolation.SERIALIZABLE:这是串行化,可以解决所有的并发问题,但是性能太低,所有事务来了,串行执行,一个一个执行,直接就没有并发事务;
注意:
1)当Spring中设置了事务隔离级别和连接的数据库事务的隔离级别的发生冲突的时候,那么以Spring事务隔离级别为准;
2)当Spring中的事务隔离级别的实现是依靠连接数据库支持事务隔离级别的基础,比如说MYSQL5.1引擎是MYSIM,MYSQL5.1默认时是不支持事务和事务隔离级别的时候,当MYSQL使用不支持事务的存储引擎的时候所以在程序里面是不支持事务和事务隔离级别的;
3)设置当前客户端的隔离级别:set session transaction isolation level +隔离级别;
1)可重复读:它能确保同一事务多次查询的结果一致,但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但它每次查询的结果都是一样的,因为他已经解决了不可重复读的问题;
2)所以会导致查询不到这条数据,自己重复插入时又失败,因为唯一约束的原因,明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读;
3)串行化:会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多;