目录
前言引入
在数据库中,通常会碰到下图中的问题,张三给李四转账,看似只需要两条sql语句即可实现,但是如果这两条sql语句一条执行成功了,一条执行失败了,这时候会发生什么情况呢,张三的钱少了,而李四的钱没有增加,凭空丢失了100,那么我们该如何应对这种情况的发生?
事务的定义:
1、事务是最小的不可分割的工作单元,通常一个事务对应着一个完整的业务
2、一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
3、事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
根据事务的定义,我们可以知道,事务是不可分割的批量的增删改语句,也就是说事务中的语句不会出现一条执行成功一条执行失败的情况,只会同时成功或同时失败
因此,我们可以通过加上事务,来解决上述的问题
事务的四大特点
1、事务具有原子性(A):指一个事物是一个不可分割的工作单位,其中的操作要么都成功,要么都失败
2、事务具有持久性(I):指事务一旦提交,它对数据库的改变就应该是永久性的,接下来的其他操作或故障不应该对其有任何影响
3、事务具有隔离性(D):隔离性是指,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰
4、事务具有一致性(C):事务执行的结果必须是使数据库从一个一致状态变到另一个一致状态
事务的使用
mysql如何实现事务:
1、开启事务
2、事务结束
3、事务提交----------------->事务回滚--------------------->只有事务出错才会回滚
#非事务提交--------------------->非事务提交无法保证业务同时成功或失败
update act set money = 0 where name = "张三";
wadasfawetae
update act set money = 200 where name = "李四";
#事务提交
#事务提交之后再进行回滚,回滚是不生效的,因为事务提交之后,事务的持久性就会起作用
start transaction; #开启事务
update act set money = 0 where name = "张三";
update act set money = 200 where name = "李四";
commit; #事务提交
rollback; #事务回滚
#提交应该在回滚之后,防止出现错误
#正确写法:
start transaction; #开启事务
update act set money = 0 where name = "张三";
update act set money = 200 where name = "李四";
#假设这里出现了错误,我们可以回滚事务
rollback;
#或者,如果没有出现错误,我们可以提交事务
commit;
事务的并发问题
1、脏读:事务B读取到了事务A修改但是未提交的数据
(如果事务A回滚了怎么办?事务B返回了一个A没有提交的数据,与事务的隔离性要求的并发执行的各个事务之间不能互相干扰相违背)
出现脏读说明事务的隔离级别很弱(读未提交)
将隔离级别设置为读已提交(事务A提交的数据,事务B才能读取到)即可解决脏读
2、不可重复读:是指在一个事务内,多次读取到同一数据,但是数据的值发生了改变
(对于事务A来说,前后两次查询到的用户1数据不一致)
3、幻读:指的是在同一个事务中,多次查询同一个范围的数据,却发现有新增或者减少的行。这是因为在这个事务进行的过程中,另一个事务插入或删除了符合查询条件的数据,导致前后两次的查询结果不一致
事务的隔离等级:为了解决并发所产生的问题,我们提出了事务的隔离级别,隔离级别越高,解决并发产生的问题越多
隔离性的隔离级别:
- 读未提交 read uncommitted
- 事物A和事物B,事物A未提交的数据,事物B可以读取到。
- 这种隔离级别最低,这种级别一般是在理论上存在,数据库隔离级别一般都高于该级别。
- 三种并发问题都没解决。
#设置读未提交的隔离级别: set global transaction isolation level read uncommitted; #查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
- 读已提交 read committed
- 事物A和事物B,事物A提交的数据,事物B才能读取到
- 这种隔离级别高于读未提交
- 换句话说,对方事物提交之后的数据,我当前事物才能读取到
- 这种级别可以避免“脏数据”
- 这种隔离级别会导致“不可重复读取”
- Oracle默认隔离级别
#设置读已提交的隔离级别: set global transaction isolation level read committed; #查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
- 可重复读 repeatable read
- 事务A和事务B,事务A提交之后的数据,事务B读取不到
- 事务B是可重复读取数据
- 这种隔离级别高于读已提交
- 换句话说,对方提交之后的数据,我还是读取不到
- 这种隔离级别可以避免“不可重复读取”,达到可重复读取
- 比如1.和2.读到数据是同一个
- MySQL默认级别
- 虽然可以达到可重复读取,但是会导致“幻像读”
#设置可重复读的隔离级别: set global transaction isolation level repeatable read; #查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
- 串行化 serializable
- 事务A和事务B,事务A在操作数据库时,事务B只能排队等待
- 这种隔离级别很少使用,吞吐量太低,用户体验差
- 这种级别可以避免“幻像读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行, 而不并发
#设置串行化的隔离级别: set global transaction isolation level serializable; #查看当前隔离级别 select @@global.tx_isolation,@@tx_isolation;
可以用一个图来更直观掌握事务的并发问题: