事务的概述
当执行一组DML操作时,为了确保数据的一致性,避免数据库产生错误数据,就需要通过事务(Transaction)使该组DML操作同时成功或失败
例如:张三有两张银行卡,卡号分别是1234567890和0987654321,现从1234567890卡取出100块钱转到0987654321卡,则取出钱的卡需要减去100,转入卡需要增加100,现实生活中这两个update操作必须同时成功同时失败,如下操作:
create table account( id char(36) primary key, card_id varchar(20) unique, name varchar(8) not null, money float(10,2) default 0 ); insert into account values('6ab71673-9502-44ba-8db0-7f625f17a67d','1234567890','张三',1000); insert into account (id,card_id,name) values('9883a53d-9127-4a9a-bdcb-96cf87afe831','0987654321','张三');
上述代码中张三的两张卡的钱数分别为1000和0,现从1234567890卡取出100块钱转到0987654321卡,则取出钱的卡需要减去100,转入卡需要增加100,现实生活中要求这两个update操作必须同时成功同时失败,如下操作:
update account set money=money-100 where card_id= '1234567890'; update account set money=money+100 where card_id= '0987654321';
当遇到执行代码时其中一个语句出现不正当的错误,代码如下:
update account set money=money-100 where card_id= '1234567890'; update account set money=mo+100 where card_id= '0987654321';
此时第二行代码没有执行成功,而第一行代码执行成功,即结果已经存到了磁盘当中。这时的数据库中的数据无法再保证账户总额还是原始的1000,莫名的丢失了100,这种情况显然不符合我们转账逻辑。这时候我们需要使用事务的知识来避免这种情况。
开启事务
在事务结束之前, 所有的 DML SQL语句都是处在同一个事务中
set autocommit=0; update account set money=money-100 where card_id= '1234567890'; update account set money=mo+100 where card_id= '0987654321';
当执行第一个DML SQL语句------>set autocommit=0;时,事务开始
开启事务之后,SQL语句执行完成后,将结果先存到临时内存中,因为没有关闭事务,磁盘中数据还没有变。
临时内存中执行查询语句,执行结果为:磁盘中的结果为(此结果需要开启另一个窗口,再执行查询语句可得到,或者直接点击数据库表查看数据):
结束事务
当出现以下情况时事务结束:
1.执行了commit或rollback语句;
commit提交事务,将执行的结果持久化到磁盘。
set autocommit=0; update account set money=money-100 where card_id= '1234567890'; update account set money=mo+100 where card_id= '0987654321'; commit;
回归数据库中的钱数1000,0,执行上述代码,结果如下:
在数据库中执行语句时,如果SQL语句出错,用commit提交事务后,依然是将结果持久化到了磁盘,此时得到的数据不严密,我们需要捕获异常。
rollback回滚事务,将DML语句执行的结果撤销
set autocommit=0; update account set money=money-100 where card_id= '1234567890'; update account set money=mo+100 where card_id= '0987654321'; rollback;
因为开启事务了,所以不执行rollback之前,没有结束事务,磁盘中数据没有变,在执行了rollback之后,因为操作失败,相当于执行结果被撤销了,所以磁盘中数据依旧是初始值。
2.执行了DDL语句(如建表语句)或DCL语句(如给用户授权),如下:
set autocommit=0; update account set money=money-100 where card_id= '1234567890'; update account set money=mo+100 where card_id= '0987654321'; create table user_in( id char(36) primary key )
上面insert语句虽然没有执行commit或rollback,但是因执行了建表语句,使得事务被隐式提交。
3.数据库客户端程序退出或数据库崩溃时,为了保持数据一致性,也会结束事务。
总结
1.执行DML语句且没有结束事务时,如果需要查询数据库的真实改变情况,则要求在一个新的SQL Window窗口中执行查询语句:因为事务中所做的每一个操作在事务被提交之前都是临时的,在commit或rollback语句执行之前,DML语句首先影响该用户的数据库缓冲区,因为这些操作可以被恢复,而且因为是在该用户的数据缓冲区,所以在原SQL窗口中查询得到的是数据缓冲区的结果,不是数据库真实的数据;要想拿到数据库真实的数据,需要在新SQL窗口中执行查询语句,这样针对不同的用户,MySQL服务器用读一致性来确保每个用户看到的数据和上次提交时的数据相同。
2.当一个用户执行DML语句时,如果其事物没有结束,则受影响的行被锁定,此时其它用户不能改变受影响行中的数据,后面用户所执行的DML语句会一直处于执行状态,直至前一个用户所执行DML语句事务结束
set autocommit=0; update account set money=money-100 where card_id= '1234567890'; delete from account where card_id= '1234567890';
上边没有关闭事务,会一直等待事务中的上一个语句执行完成。 没有关闭事务,在同一个窗口执行俩条语句,相当于两个语句在同一个事务中,都会执行成功。 执行成功,而且将结果提交,持久化到磁盘。
事务的特性
原子性(Atomicity):原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务操作如果成功就完全应用到数据库,否则不会对数据库有任何影响。
一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态,即一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还是5000,这就是事务的一致性。
隔离性(Isolation):当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离,即对于任意两个并发事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
持久性(Durability):一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。