简单的说:事务就是将一堆SQL(通常是增删改操作)的执行绑在一起,要么都执行成功,要么都执行失败,即都执行成功才算成功,否则就会恢复到这堆SQL执行之前的状态。
转账:AB 总1000元,转账100元
A:update account set money=money-100 where name=’A’
B: update account set money=money+100 where name=’B’
如果在第一条SQL语句执行成功后,在执行第二条SQL语句之前,程序被中断了(可能是抛出了某个异常,也可能是其他什么原因),那么B的账户没有加上100元,而A账户却减去了100元。这肯定是不行的。
你现在可能已经知道什么是事务了吧!在上面整个转账过程中执行的所有SQL语句会在一个事务中,而事务中的多个操作,要么全都成功,要么全都失败,不可能存在成功一半的情况。
也就是说给A的账户减去100元如果成功了,那么给B的账户加上100元的操作也必须是成功的;否则,给张三减去100元以及给李四加上100元都是失败的。
事务的四大特性
事务的四大特性(ACID)是:
(1) 原子性(Atomicity):事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。(其中的所有SQL语句,要么全部执行成功,要么全部执行失败!!!)
(2) 一致性(Consistency):
事务前后的业务数据是保持一致的,(转账前后数据总和不变,转账之前A和B账户数据总和是2000元,不管转账之后操作成功还是失败,只要在同一个事务里面,数据总和还是2000元)
转账:AB 总1000元,转账100元
A:1000-100=900
B:1000+100=1100 两个账户转账成功后总和是2000元
两个账户转账失败:
A:1000-100=900,900+100=1000
B:1000(断电了) 金额总和还是2000元
事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号金额之和应该是不变的。
(3) 隔离性(Isolation):
多个事务之间是相互隔离开来的,一个事务不会访问到另外一个事务正在执行中的操作。
事务1:转账100元 A:1000-100=900;B:1000+100=1100
事务2:查询A,B的账号金额(能看到的数据,要么是事务1在转账之前的,要么是转账之后的状态,不能看到中间状态)
隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。也就是说,在事中务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。例如:在A事务中,查看另一B事务(正在修改张三的账户金额)中张三的账户金额,要查看到B事务之前的张三的账户金额,要么查看到B事务之后张三的账户金额。
(4) 持久性(Durability):
如果事务一旦提交(事务中的SQL语句全部都执行成功了),修改后的数据就真的被保存到了数据库中(硬盘上)
开启事务-----
1000-100=900 数据被临时写在日志里面
1000+100=1100 数据被临时写在日志里面
提交/回滚
一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
MySQL中的事务
在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。如果需要在一个事务中包含多条SQL语句,那么需要开启事务和结束事务。
开启事务:start transaction;
结束事务:commit(提交事务)或rollback(回滚事务)。
在执行SQL语句之前,先执行strat transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条SQL语句所做出的影响会持久化到数据库中。或者rollback,表示回滚,即回滚到事务的起点,之前做的所有操作都被撤消了!
事务并发读问题:
在事务并发时,如果没有采取必要的隔离措施,可能会导致各种并发问题,破坏数据的完整性等。这些问题中,其中有三类是读问题,分别是:脏读、不可重复读、幻读。
(1)脏读(dirty read):
读到另一个事务的未提交更新数据,即读取到了脏数据;
B:卖家
A:买家
Start transaction
1000-100=900 A
1000+100=1100 B
事务未提交 B查询到钱已经打过来了,就发货了
Rollback; A进行事务的回滚,(事务的隔离等级不允许)
(2)不可重复读(unrepeatable read):
对同一记录的两次读取不一致,因为另一事务对该记录做了修改(是针对修改操作)
事务1: select * from acc where name=’A’ --1000
执行事务2:
–开启事务
A 1000-100 =900
–提交事务
再次查询事务1 的数据 变为了900
(3)幻读(虚读)(phantom read):
对同一张表的两次查询不一致,因为另一事务插入了一条记录(是针对插入或删除操作);
事务1:
Select * from acc where id=3; --null
Insert into acc values(3,’C’,1000); --插入数据之前,事务2执行了
Select * from acc where id = 3; --1条记录 --再次查询数据会产生一条记录
事务2:
Insert into acc values(3,’C’,1000);
注意:mysql默认的是不允许出现脏读和不可重复读,所以在下面演示之前需要设置mysql允许出现脏读、不可重复读等。
事务隔离级别
事务隔离级别分四个等级,在相同数据环境下,对数据执行相同的操作,设置不同的隔离级别,可能导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力也是不同的。
set tx_isolation=‘read-uncommitted’;
1、READ UNCOMMITTED(读未提交数据)
安全级别最低, 可能出现任何事务并发问题(比如脏读、不可以重复读、幻读等)
性能最好(不使用!!)
2、READ COMMITTED(读已提交数据)(Oracle默认)
防止脏读,没有处理不可重复读,也没有处理幻读;
性能比REPEATABLE READ好
3、REPEATABLE READ(可重复读)(MySQL默认)
防止脏读和不可重复读,不能处理幻读问题;
性能比SERIALIZABLE好
4、SERIALIZABLE(串行化)
不会出现任何并发问题,因为它是对同一数据的访问是串行的,非并发访问的;
性能最差;
MySQL的默认隔离级别为REPEATABLE READ,即可以防止脏读和不可重复读