一、事务的概述
事务:指的是逻辑上的一组操作,组成这组操作的各个逻辑单元要么全部成功、要么全部失败
二、MYSQL命令行窗口事务的常用操作
1.开启事务: start transaction
2.提交事务: commit
3.回滚事务: rollback
三、事务的特性
1.原子性
组成事务的各个逻辑单元不可分割,事务包含的所有操作要么全部成功,要么全部失败回滚;成功必须要完全应用到数据库,失败则不能对数据库产生影响;
2.隔离性
当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不被其他事务的操作所干扰,多个并发事务之间要相互隔离;
3.持久性
一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便在数据库系统遇到故障的情况下也不会丢失事物的操作。
4.一致性
事务执行前和执行后必须处于一致性状态, 例:用户A和用户B的前加起来一共是5000; 无论AB用户之间是如何相互转换的,事务结束后两个用户的钱加起来还是5000,这就是事务的一致性。
四、隔离级别
隔离性:当多个线程都开启事务来操作数据库中的数据时,数据库系统要进行隔离操作,以保证各个线程获取数据的准确性。 不考虑事务的隔离性,会产生的几种问题:
1:脏读
一个事务读到了另一个事务未提交的数据 ,导致多次查询结果不一致
例:用户A向用户B转账100元,A通知B查看账户,B发现前确实已到账,而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,当B再次查看账户时就会发现前其实并没有到账。
2:不可重复读
一个事务读到了另一个事务已经提交的update的数据,导致多次查询结果不一致。
不可重复读是指在一个事务内,多次读取同一个数据,在这个事务还没有结束 ,另一个事务也访问该同一数据,但是由于第二个事务的修改,那么第一个事务两次读取的数据可能不一样,因此称为不可重复读;即同一个事务中原始数据读取不可重复。
注:不可重复读和脏读的区别,脏读是某一个事务读取另一个事务未提交的脏数据; 不可重复读则是读取前一事务提交的数据
3:幻读:
一个事务读到了另一个事务已经提交的insert的数据,导致多次查询结果不一致。
当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行修改,这种数据涉及到表中的全部数据行,同时,第二个事务也对这个表数据进行修改,这个修改是对表中新增/删除一条数据,那么操作第一个事务的用户发现表中的数据还没有修改的数据行,就好像发生了幻觉一样,这就是发生了幻读。
注:幻读和不可重复读都读取另一条已经提交的事务,所不同的是不可重复读查询的都是同一数据项,而幻读针对的是一批数据整体。
数据库提供的四种隔离级别:
01:Read uncommitted(读未提交):最低级别,任何情况都会发生。
02:Read Committed(读已提交):可避免脏读的发生。(oracle默认级别)
03:Repeatable read(可重复读):可避免脏读、不可重复读的发生。(mysql默认级别)
04:Serializable(串行化):避免脏读、不可重复读,幻读的发生。
注: 四种隔离级别最高:Seralizable级别,最低的是Read uncommitted级别; 级别越高,执行效率就越低; 隔离级别的设置只对当前链接有效,对JDBC操作数据库来说,一个Connection对象相当于一个链接,只对该Connection对象设置的隔离级别只对该connection对象有效,与其它链接connection对象无关。
01:Mysql的默认隔离级别是:可重复读:Repeatable read;
02:oracle数据库中,只支持seralizable(串行化)级别和Read committed();默认的是Read committed级别;
五、安全问题演示
【事物】演示脏读
- 开启两个窗口A,B
- 设置A窗口的隔离级别为read uncommitted;
SET SESSION TRANSACTION ISOLATION LEVEL read uncommitted;
- 在A,B两个窗口中开启事务
start transaction;
- 在B窗口中完成转账的功能:
update account set money = money - 1000 where name= '小张';
update account set money = money + 1000 where name= '小凤';
-- 这里不提交事物
- 在A窗口中进行查询
select * from account;
发现A窗口中已经查询到转账成功了!!!已经发生了脏读:一个事务中已经读到了另一个事务未提交的数据。
- 开启两个窗口A,B
- 设置A窗口的隔离级别为read committed;
SET SESSION TRANSACTION ISOLATION LEVEL read committed;
- 分别在两个窗口中开启事务:
start transaction;
- 在B窗口中完成转账
update account set money = money - 1000 where name= '小张';
update account set money = money + 1000 where name= '小凤';
-- 没有提交事物
- 在A窗口中进行查询:
select * from account;
发现这个时候没有转账成功!!!(没有查询到另一个事务未提交的数据:说明已经避免了脏读)。
- 在B窗口中提交事务
commit;
- 在A窗口查询
select * from account;
发现这次的结果已经发生了变化!!!(已经发生不可重复读:一个事务已经读到了另一个事务提交的update的数据,导致多次查询结果不一
- 分别开启两个窗口A,B
- 设置A窗口的隔离级别:repeatable read;
SET SESSION TRANSACTION ISOLATION LEVEL repeatable read;
- 在A,B两个窗口中开启事务:
start transaction;
- 在B窗口完成转账
update account set money = money - 1000 where name= '小张';
update account set money = money + 1000 where name= '小凤';
-- 没有提交事物
- 在A窗口中进行查询
select * from account;
发现没有转账成功:说明避免脏读!!!
- 在B窗口中提交事务
commit;
- 在A窗口中再次查询:
select * from account;
发现在一个事务中的多次查询结果是一致!!!(已经避免不可重复读)
- 开启两个窗口A,B
- 设置A窗口的隔离级别:serializable
- SET SESSION TRANSACTION ISOLATION LEVEL serializable;
- 分别在两个窗口中开启事务:
- start transaction;
- 在B窗口中插入一条记录
- insert into account values (null,'小李',10000);
- 在A窗口中进行查询
- select * from account;
发现A窗口已经卡住了(说明事务不允许出现并发,A窗口需要等待B窗口事务执行完成以后,才会执行A窗口的事务。)当B窗口的事务结束(提交或者回滚),那么A窗口马上就会出现结果。