数据库中有事务的概念,那么什么是事务呢?
事务指的是一组操作的集合,事务会把集合中所有的操作看成是一个整体,一起向数据库提交这些操作,或者撤销这些操作,这些操作可以是增加数据、修改数据、插入数据、查询数据,但是这一个集合中的所有操作必须要么全部成功,要么全部失败。
事务的重点概念就是:要么全部执行成功、要么全部执行失败。
事务这种操作经常应用于银行、金融等业务场景,只要涉及金额、交易等这类场景,就需要用到事务操作。
为了更清楚的理解什么是事务,我们一起来看下面这个场景。
小明的账户里有5000元,要给小红转账1000元,此时数据库就开启了一组事务,事务中分为了三个操作模式:
- 首先查询小明账户中的余额是否满足要转账的金额。
- 然后触发更新数据的操作,小明的账户减少1000元。
- 最后再次触发一个更新数据的操作,小红账户增加1000元。
事务中的三个操作要么全部执行成功,要么全部执行失败,当三个操作全部执行成功时,数据库会提交这个事务,如果中间有一个操作执行失败了,会立刻回滚事务,将修改过的数据全部还原,防止业务逻辑出现问题。
如果其中某个操作异常了,其他的都提交到数据库了,那么就会导致数据异常,金融行业的数据异常是万万不能存在的。
事务在MySQL中是默认开启的,默认情况系啊,每一条DML语句,都是一条事务。
事务的应用有两种方式:
控制事务的提交方式(手动、自动。)
开启事务、提交事务、回滚事务。
事务的四大特性分别是原子性、一致性、隔离性、持久性。
创建一张金融数据表,里面有小明和小红两个人,分别有2000块钱的余额,小明给小红转账一千元,我们针对转账场景模拟事务,观察事务的表现方式。
create table yexxb (
id int primary key auto_increment comment 'ID',
xm varchar(10) comment '姓名',
ye double(10,2) comment '余额'
) comment '余额信息表'
insert into yexxb (xm,ye) values ('小明','2000'),('小红','2000');
首先来测试一下未控制事务的情况下,数据的一个执行状态。
下面的SQL语句是小明给小红转账的完整语句流程,全部选中一起执行。
-- 查询小明的余额
select * from yexxb where xm = '小明';
-- 将小明的余额减少1000
update yexxb set ye = ye - 1000 where xm = '小明';
-- 将小红的余额增加1000
update yexxb set ye ye + 1000 where xm = '小红';
正常情况下,SQL语句全部执行成功,业务逻辑也没有任何问题,小明给小红转账后,小明的余额减少了1000元,小红的余额增加了1000元,没有任何问题。
业务逻辑并不是时时刻刻都能够成功执行的,也有很多因为网络问题,程序产生了异常,三个SQL操作,只有某一个成功了,那么就会导致数据异常。
选中下面所有的SQL语句,全部一起执行,模拟不正常情况下事务的现象。
-- 查询小明的余额
select * from yexxb where xm = '小明';
-- 将小明的余额减少1000
update yexxb set ye = ye - 1000 where xm = '小明';
程序异常,网络波动
-- 将小红的余额增加1000
update yexxb set ye ye + 1000 where xm = '小红';
由于网络或者程序的异常,导致应该三个都需要执行成功的SQL,此时只执行成功了前两个,第三条并没有执行成功,此时就会导致小明的余额虽然减少了1000元,但是小红的余额却没有增加。
这在金融行业是绝对不允许发生的现象,涉及到钱的问题都不是小问题。
此时我们也可以得到一个结论,在默认的情况下,MySQL数据库中的一条SQL语句,都是一个事务,执行完一条SQL后,都会提交到数据后,对数据进行操作,当遇到多SQL有关联性,需要一起实现某个业务逻辑时,如果还使用默认的事务配置,那么就会对数据产生不可逆的影响。
1)设置事务的提交方式
SET @@autocommit = 0|1
1:自动提交事务,1条SQL表示1个事务
0:手动提交事务,不提交事务,数据就不会发生改变
当设置了手动提交事务的行为后,此时我们执行的SQL语句不会提交到数据库,数据不会发生修改,需要手动执行提交事务的命令。
2)查看事务的提交方式
SELECT @@autocommit
3)提交事务
COMMIT
4)回滚事务
ROLLBACK
在前面不控制事务时,当有网络波动,数据就会执行异常,下面我们手动控制事务的提交,来观察控制事务后转账场景的效果。
set @@autocommit = 0;
此时同时执行下面的SQL,下面的SQL就是完整的转账流程,观察手动控制事务的提交后,数据有什么变化。
-- 查询小明的余额
select * from yexxb where xm = '小明';
-- 将小明的余额减少1000
update yexxb set ye = ye - 1000 where xm = '小明';
程序异常,网络波动
-- 将小红的余额增加1000
update 网络波动yexxb set ye ye + 1000 where xm = '小红';
和前面没有控制事务时,有了很明显的编号,当手动控制了事务的提交后,只要我们不提交数据,无论执行了多少个更新数据的操作,数据表中的数据永远都不会发生改变。
下面我们来执行提交事务的操作,然后再观察表中的数据。
commit;
当事务提交成功后,数据库表中的数据才会执行对应的操作,否则任何处理数据的动作只会存在于当前会话中(在这个会话中使用select就可以看到修改后的数据,但是不提交就不会再表中进行更改)。
在前面演示了正常情况下的手动控制事务提交的现象,下面来看看非正常情况时,手动控制事务的现象。
此时执行下面的所有SQL时,就会看到异常的现象,此时,第三条SQL是不会执行成功的,但是前面两条都是成功的。
-- 查询小明的余额
select * from yexxb where xm = '小明';
-- 将小明的余额减少1000
update yexxb set ye = ye - 1000 where xm = '小明';
程序异常,网络波动
-- 将小红的余额增加1000
update 网络波动yexxb set ye ye + 1000 where xm = '小红';
我们还没有提交事务,因此当发现异常时,可以执行回滚的操作,将事务撤销,保证数据不受影响。
rollback;
可以看到此时的数据时不会受到任何影响的。
效果和手动控制事务提交行为一样,重点是语法不同,使用手动开启事务的方式时,无需调整事务提交的默认行为。
1.开启事务
start transaction
2.执行事务的SQL逻辑
-- 查询小明的余额
select * from yexxb where xm = '小明';
-- 将小明的余额减少1000
update yexxb set ye = ye - 1000 where xm = '小明';
-- 将小红的余额增加1000
update set ye = ye + 1000 where xm = '小红';
3.提交事务
commit
4.如果SQL执行异常那么就回滚事务
rollback
在实际的生产环境中,建议使用此种方式。