事务
MySQL中InnoDB存储引擎支持事务
事务( Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地一起执行,作用在数据库上,使数据库永远的修改;要么完全地不执行,对数据库不做任何改变。
例如银行之间的汇款操作,该操作在数据库上通过3步完成。
(1)源账户减少储存金额
(2)目标账户增加金额
(3)在事务日志中记录该事务
整个交易过程就可以看成一个事务,如果操作失败,那么该事务就会全部回滚,所有该事务中的操作撤销;如果操作成功,那么就对数据库进行永久的修改
事务的特性 ACID
原子性(Atomicity)
事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
一致性(Consistency)
事务在完成时,必须使所有的数据都保持一致状态。只有在事物完成之后才能被所有使用者看见,保证了数据的完整性。例如从账户A转一笔钱到账户B上,如果账户A上的钱减少了,而账户B上的钱却没有增加,那么我们认为此时数据处于不一致的状态。
隔离性(Insulation)
由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。也就是说一个事务操作的数据不会被其他实物看到和操作
持久性(Durability)
事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。
事务的模式类型
可以用 START TRANSACTION 开始一个事务
COMMIT 提交实物并将修改的数据持久保留
ROLLBACK 撤销所有修改
MySQL采用的是自动提交(AUTOCOMMIT)的模式。也就是说,如果不是显示地开始一个事务,则每个增,删,改都被当作一个事务执行提交操作
样本
START TRANSACTION;
SELECT balance FROM checking WHERE customer_id =1000;
UPDATE checking SET balance = balance -200.00 WHERE customer_id = 1000;
UPDATE saving SET balance = balance + 200.00 WHERE customer_id = 1000;
COMMIT;
COMMIT AND CHAIN 表示提交实物之后重新开启了一个新的事务
START TRANSACTION;
...
(操作语句)
...
COMMIT AND CHAIN;
--提交的同时开启一个新的事务
...
(操作语句)
...
COMMIT;
保存点
由于某些情况下业务逻辑过于复杂,需要很多操作才能实现某项业务,为了避免每次失败都回滚所有数据,就可以选择事务的保存点。
SET AUTOCOMMIT =0;
-- 关闭自动提交 1代表启用 0代表关闭。
INSERT INTO userinfo VALUES(6,'test1');
savepoint s1;
INSERT INTO userinfo VALUES (7,'test2');
savepoint s2;
INSERT INOT userinfo VALUES (8,'test3');
savepoint s3;
执行完三个插入语句,如果想回滚到最初,ROLLBACK就是回滚到最初状态,如果想回滚到savepoint s1 的状态(就是插入test1那里)rollback to savepoint s1 就可以了。
并发控制
当并发访问数据时,如果不加以控制,那么修改的数据将有可能影响到同意时间读取或者修改相同数据的其他用户。
我们先看看如果不考虑事务的隔离性,会发生的几种问题:
(1)脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
例如:事务A正在修改数据库中的某行数据,在修改过程中,事务B读取了该行数据,并保留准备进行下一步操作。但是此时事务A认为自己修改是事务不准确,ROLLBACK了。这种情况下事务B则读取到了实际上不准在的数据(脏数据)。
解决方法:在A事务提交实物之前,不允许其他事务读取正在改动的数据。
(2)不可重复读
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
例如事务A在读取某一数据,而事务B立马修改了这个数据并且提交事务给数据库,事务A再次读取该数据就得到了不同的结果,发送了不可重复读。
不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
解决方法:事务A完成最后一次读取数据之前,不允许其他事务修改正在读取的数据。
(3)幻读
幻读是事务非独立执行时发生的一种现象。例如事务A对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务B又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务A的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务B中添加的,就好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
现在来看看MySQL数据库为我们提供的四种隔离级别:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。串行化会在读取的每一行数据上都加锁,所以会造成锁超时和锁占用问题,在实际业务中很少使用,除非要求严格数据一致性,并没有并发。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库innodb中默认的隔离级别为Repeatable read (可重复读)。
查看当前隔离级别
select @@tx_isolation;
设置事务的隔离 级别
set [glogal | session] transaction isolation level 隔离级别名称;
set tx_isolation='隔离级别名称';