事务简述
什么是事务? 事务是一组操作的集合,它是一个不可分割的工作单位。当我们在进行事务的操作时,事务会把所有的操作作为一个整体一起向操作系统提交或撤销操作请求,即这些操作要么全部成功,要么全部失败。 比较典型的案例就是“银行转账”。 正常情况下转账操作分为三步: 1、小明查询余额 2、小明的余额 -100 3、小红的余额 +100 异常情况下,如果小明的余额 -100 后突发异常,小红没有收到100块钱,小明的钱就不翼而飞了。 对于这种问题,我们就要使用MySQL中的事务。 对于转账操作 1、小明查询余额 2、小明的余额 -100 3、小红的余额 +100 这三步要么全部执行成功,要么全部执行失败。所以我们要把这三个步骤控制到一个事务范围内。 但是我们要怎么实现“要么全部成功,要么全部失败”呢? 首先我们要知道,在MySQL中,一个操作是默认提交的,即 执行->提交 ,
而我们要做的,就是先让他不提交,确认一个事务全部完成时再提交。 就像如下:
即执行这组操作前我们先开启事务,若事务内的全部步骤都顺利完成,才把这些步骤造成的影响提交给数据库。 但是万一出现异常情况呢?此时就要“回滚事务”,即:把出现异常之前修改的数据修改回去
那么我们如何将提交放到最后,又如何在出现异常后实现回滚呢?
事务操作演示
方式一
使用刚才所说的转账案例,代码如下:
create table account (
id int ,
name varchar(10) ,
balance int
);
insert into account (id, name, balance) VALUES (1, '小明', 200);
insert into account (id, name, balance) VALUES (2, '小红', 200);
现在我们面临三个问题: ① 如何让这三步操作成为一个事务? ② 如何人为制造“异常”? ③ 出现异常后如何回滚?
一 : 我们刚才说过,正因为每一次操作都自动提交,所以每一次操作都称为一个事务,我们只要先把“自动提交”关掉,在三步操作顺利完成之后再手动提交,就可以使这三步成为一个事务。 如何关闭自动提交? 查看/设置事务提交方式:
select @@autocommit;
set @@autocommit = 0;
提交事务:
commit;
二 :如何制造异常? 我们只需要在需要的地方写出一条错误的sql语句就可以看成是“异常”。
在执行后,因为我们的commit没有执行,所以数据不会发生改变。
这里有一个疑问 :没有提交,数据没有变,为什么还非要回滚呢? 不回滚 :你的数据其实改变了,但是你没有提交,你提交之后小明的钱依然会扣。 回滚 :你的数据恢复成原来的样子。
rollback;
方法二
开启事务:
start transaction;
提交事务:
commit;
回滚:
rollback;
只要出现异常,我们要做的是回滚而不是提交。
事务的特性ACID
原子性(Atomicity):事务是不可分割的最小单元,要么全部成功,要么全部失败。 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。 拿转账案例来说,执行事务前总余额为400,不管事务执行成功并提交还是执行失败并回滚,总余额还是400。 隔离性(Isolation):数据库提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。 持久性(Duability):事务一旦提交或回滚,他对数据库的改变就是永久的。
并发事务问题
为了更加直观的显示隔离对于事务并发问题的解决,这里同时使用DateGrip和cmd命令行来模拟两个并发事务。DateGrip模拟事务A,cmd命令行模拟事务B。
1、脏读
一个事务读到了另外一个事务还没有提交的数据。
对于两个事务。 ① 事务A的操作1将数据库中的某条数据修改(事务A还未结束,所以还未提交) ② 事务B的操作1读取了事务A修改但未提交的数据
2、不可重复读
一个事务先后读取同一条数据,但两次读取的数据不同,称为不可重复读。
在并发事务中 1、事务A查询余额为200 2、此时事务B更改余额并提交 3、事务A再次查询时余额已经变成100 同一个事务的两次查询结果不同,这就是“不可重复读”。
3、幻读
一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了“幻影”。
在并发事务中 1、事务A的操作1先查询是否存在 id = 3 的数据,结果是不存在(不存在就准备插入一条呢) 2、事务B的直接插入一条 id = 3 的数据。 3、事务A的操作2想插入一条 id = 3 的数据,突然发现 id = 3 的数据已经存在。如果此时还没有解决“不可重复读”的问题还好 4、但是如果已经解决“不可重复读”的问题(即同一个事务中同样的查询操作查询到的数据相同),假如事务A的操作3再次查询是否存在,结果是“不存在”。 这就是“幻读” 。 再来捋捋事务A :第一步查询 id = 3 的数据显示“不存在”;第二步插入 id = 3的数据,插不进去;第三步再次查询是否存在 id = 3 的数据,显示“不存在”。插入操作的前后两次查询都显示“不存在”,但就是插不进去。
事务的隔离级别
事务的隔离级别就是用于解决事物的并发问题。 先来看看事务的隔离级别有哪几种,和他们能解决什么样的事务并发问题。 √ :会出现 × :不会出现
事务的隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|
Read uncommitted | √ | √ | √ | Read committed | × | √ | √ | Repeatable Read(默认) | × | × | √ | Serializable | × | × | × |
(Serializable:串行化,是拒绝并发,相当于给事务加上锁) 从上到下虽然隔离级别依次变高,但是性能逐渐降低 查看事务的隔离级别
select @@transaction_isolation;
设置事物的隔离级别
set [session|global] transaction isolation level [隔离级别];
session :仅对当前窗口有效 global :对所有客户端的窗口有效
|