1. 事务的基本介绍
如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
2. 操作:
1. 开启事务: start transaction;
2. 回 滚:rollback;
3. 提 交:commit;
3.案例:张三给李四转500元
张三余额是否够500
张三 -500
异常
李四 +500CREATE TABLE account( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(10), balance DOUBLE ); -- 添加数据 INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000); SELECT * FROM account;
正常情况:
UPDATE account SET balance = balance - 500 WHERE NAME='zhangsan'; UPDATE account SET balance = balance + 500 WHERE NAME='lisi';
异常情况:UPDATE account SET balance = balance - 500 WHERE NAME='zhangsan'; 出错啦! UPDATE account SET balance = balance + 500 WHERE NAME='lisi';
出现这个问题是因为在汉字的地方被当成 SQL 语句执行了,zhangsan已经转账500, 而lisi却没有收到500
解决: 需要用到事务解决
UPDATE account SET balance = 1000; -- 复原账务
关于事务的情况
第一种情况: 异常情况下, 开启了事务,不进行提交,不进行回滚
-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 出现异常了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
扫描二维码关注公众号,回复: 5467373 查看本文章结果: 数据发生临时变化,打开两个窗口时,另一个窗口数据是不变的,或者 重启发生数据改变的SQLyog工具后临时变化前的数据
第二种情况 正常情况下 不提交事务也不需要回滚-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 -- 出错了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
结果: 转账成功, 另一个窗口没有进行转账,重启转账成功的窗口发现转账是临时的操作,原因是没有进行提交事务
第三种情况: 正常情况下.提交事务
-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 -- 出错了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi'; -- 发现执行没有问题,提交事务 COMMIT;
结果: 在正常情况下转账成功, 事务进行了提交, 两个页面查询都正常转账,重启后转账也显示成功
第四种情况,异常情况下进行回滚事务 不提交-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 出现异常了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi'; -- 发现出问题了,回滚事务 ROLLBACK;
结果:转账不成功,数据不变,事务回到了回滚开启事务的地方
第五种情况: 数据正常, 开启事务 ,提交事务, 回滚事务-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 -- 出错了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi'; -- 发现执行没有问题,提交事务 COMMIT; -- 发现出问题了,回滚事务 ROLLBACK;
结果: 转账成功并提交事务
第六种情况:数据异常, 开启事务 ,提交事务, 回滚事务-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 出错了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi'; -- 发现执行没有问题,提交事务 COMMIT; -- 发现出问题了,回滚事务 ROLLBACK;
结果: 转账异常 张三进行了-500, 李四没有+500 ,事务进行了提交,没有回滚
第七种情况: 数据异常, 开启事务 ,回滚事务,提交事务 (注意和第六种情况的顺序不同)-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 出错了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi'; -- 发现出问题了,回滚事务 ROLLBACK; -- 发现执行没有问题,提交事务 COMMIT;
问题: 错误的情况下进行转账回滚事务, 达到了预期结果,错误就进行了事务回滚
第八种情况: 数据正常, 开启事务 ,回滚事务,提交事务 (注意和第六种情况的顺序不同)-- 开启事务 START TRANSACTION; -- 张三账户 -500 UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan'; -- 李四账户 +500 -- 出错了... UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi'; -- 发现出问题了,回滚事务 ROLLBACK; -- 发现执行没有问题,提交事务 COMMIT;
结果: 正确的数据下转账应该成功却没有成功,数据没有变化,也就是说回滚关键字起作用了
总结:
问题: 错误的情况下进行转账回滚事务,为什么正确的数据下还是回滚了,这个ROLLBACK和COMMIT不能同时存在吗?注意: 两个关键字是不能同时存在的,要么进行了提交, 要么进行了回滚, 二选一
4. MySQL数据库中事务默认自动提交
1)事务提交的两种方式:
自动提交:
mysql就是自动提交的
一条DML(增删改)语句会自动提交一次事务。例如进行update 更改一条数据 ,自动提交了事务, 数据会持久化更新
手动进行了开启事务(start transaction;),就需要手动提交事务(commit;)
手动提交:
Oracle 数据库默认是手动提交事务
需要先开启事务,再提交
2) 修改事务的默认提交方式:
查看事务的默认提交方式:SELECT @@autocommit; -- 1 代表自动提交 0 代表手动提交
修改默认提交方式: set @@autocommit = 0;
验证: 修改事务提交方式为0(手动提交),表account的原数据中是1000
进行更改数据为888,修改后因为不是自动提交事务,关闭SQLyog再打开查看数据还是原来的1000
重新打开,查询后数据如下图:
5. 事务的四大特征:
1) 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
2) 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
3) 隔离性:多个事务之间。相互独立。
4) 一致性:事务操作前后,数据总量不变
6. 事务的隔离级别(了解)
概念:多个事务之间是隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
存在问题:
1. 脏读:一个事务,读取到另一个事务中没有提交的数据( 另一个数据没有进行提交就读到了,很严重的问题)
2. 不可重复读(虚读):在同一个事务中,两次读取到的数据不一样。
3. 幻读:一个事务操作(DML)数据表中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
隔离级别:
1. read uncommitted:读未提交
产生的问题:脏读、虚读、幻读
2. read committed:读已提交 (Oracle)
产生的问题:虚读、幻读
3. repeatable read:可重复读 (MySQL默认)
产生的问题:幻读
4. serializable:串行化
可以解决所有的问题
注意:隔离级别从小到大安全性越来越高,但是效率越来越低
数据库查询隔离级别:
select @@tx_isolation;
数据库设置隔离级别:
set global transaction isolation level 隔离级别字符串;
验证1:把事务的隔离级别设置成read uncommitted:读未提交
set global transaction isolation level read uncommitted;
设置隔离级别后,连接需要断掉,重新打开sqlyog才会生效;
第一步: 打开两个窗口, 都是原数据1000, 都手动提交事务 start transaction;
第一个窗口进行-- 转账操作
update account set balance = balance - 500 where id = 1;
update account set balance = balance + 500 where id = 2;
第二步: 第一个窗口并未提交事务,查询第二个窗口, 转账成功, 读未提交隔离级别生效, 数据查到变化这是发生了脏读
第二个窗口读到了第一个窗口没有提交的数据,脏读发生了
此时第一个窗口没有提交而是进行了回滚操作rollback,数据还原到转账前的操作,第二个窗口的数据变回去了
脏读发生了,不可重复读(虚读)也发生了, 第一个窗口查到的数据不一样
幻读在MySQL中演示不出来
验证2:把事务的隔离级别设置成read committed;读已提交 解决脏读,但是还有虚读
set global transaction isolation level read committed; -- 设置隔离级别
select @@tx_isolation; -- 查询隔离级别
第一步: 打开两个窗口: 都开始事务START TRANSACTION;,数据都是1000
第二步: 转账
UPDATE account SET balance = balance - 500 WHERE id=1;
UPDATE account SET balance = balance + 500 WHERE id=2';
第一个窗口进行转账操作, 第二个窗口查不到转账还是原来的数据1000,
需要第一个窗口进行提交事务,第二个窗口才可以查到, 解决了脏读问题
第二个窗口第一次查到的是1000,1000 第二次查到的是500, 1500 存在不可重复读
验证3:把事务的隔离级别设置成repeatable read;可重复读 解决脏读,虚读,还存在幻读
set global transaction isolation level repeatable read; -- 设置隔离级别
select @@tx_isolation; -- 查询隔离级别
数据复原1000,都开启事务
第一步: 第一个窗口开启了事务,第二个窗口也开启了事务
第一个窗口进行转账操作,未提交事务
UPDATE account SET balance = balance - 500 WHERE id=1;
UPDATE account SET balance = balance + 500 WHERE id=2';
第二个窗口查询是1000,1000
第二步: 第一个窗口进行了提交commit, 第二个窗口进行查询还是1000,1000
第二个窗口可重复读隔离级别生效了,两次查询的结果都是一样的
在同一个事务中(两个窗口分别各开启了事务), 当在第二个窗口提交事务后或者回滚后才可以查到数据变化
验证4:把事务的隔离级别设置成serializable; 串行(hang)化 解决脏读,虚读,幻读
串行化就是锁表的动作,如果一个事务在操作一张表,另外一个事务是不能操作这张表的
只有当这把锁打开之后另外一个事务才可以操作
set global transaction isolation level serializable; -- 设置隔离级别
select @@tx_isolation; -- 查询隔离级别
数据复原1000,都开启事务
第一步:都开启了事务 第一个窗口转账操作,未提交, 第二个窗口进行查询,并没有执行查询语句一直在等待
这是因为串行化隔离级别生效了,只有第一个窗口进行了回滚或者是提交之后才可以进行查询
第一个窗口进行提交事务(释放锁)后, 第二个窗口才可以进行查询数据
第一个把锁给锁上了,只能在这个事务下操作,别的开启事务后只有等待,效率很低
所以隔离级别越来越安全,效率越来越低
<事务完 >