事务,是由一条或多条SQL 语句组成的一个整体,这些SQL语句要么都执行成功,要么都执行失败,只要有一条SQL出现异常,整个操作就会进行回滚,整个业务执行失败。
回滚:在事务运行过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库已完成的操作全部撤销,滚回到事务开始之前的状态。
现有一项业务,tom 要给 jerry 的银行卡中转 500 块钱,那么银行就至少需要操作两次数据库,第一次 tom 账户余额 - 500元,第二次 jerry 账户 + 500 元,且要保证不出任何问题,才能保证银行和个人都无任何损失。
-- 创建账户表
CREATE TABLE ACCOUNT(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
money DOUBLE
);
-- 向表中插入两个用户
INSERT INTO ACCOUNT(NAME,money) VALUES('tom',1000),('jerry',1000);
-- tom账户余额-500
UPDATE ACCOUNT SET money = money - 500 WHERE NAME = 'tom';
-- jerry账户余额+500
UPDATE ACCOUNT SET money = money + 500 WHERE NAME = 'jerry';
注意:
当在执行任意一条语句时出现问题,都会造成银行或用户的损失,所以必须保证整个事务执行的完整性,要么都成功,要么都失败。
MySQL事务操作的方式有两种:
语法:
start transcation
开启事务(或begin)
commit
提交事务
rollback
回滚事务
select * from account;
start transaction;
-- tom账户余额-500
update account set money = money - 500 where name = 'tom';
-- jerry账户余额+500
update account set money = money + 500 where name = 'jerry';
commit;
如果事务中,某条sql语句执行时报错了,我们没有手动提交事务,那么整个事务会自动回滚
start transaction;
INSERT INTO ACCOUNT(NAME,money) VALUES('tony',1000);
INSERT INTO ACCOUNT(NAME,money) VALUES('jack',1000);
MySQL默认每一条DML语句都是一个单独的事务,每条语句会自动开启一个事务,语句执行完毕自动提交事务,MySQL默认是自动提交事务。
自动提交事务演示
show variables like 'autocommit';
把autocommit修改为off
set @@autocommit=off;
各个事务之间是相互隔离的,但是如果多个事务对数据库中的同一批数据进行并发访问的时候,就会引发一些问题,这时就需要设置不同的隔离级别来解决对应的问题。
并发访问的问题
隔离级别
注意
:隔离级别从小到大,隔离级别越来越高,执行效率越来越低,根据不同的情况选择不同的隔离级别。
隔离级别相关的命令
select @@transcation_isolation;
set global transcation isolation level 隔离级别;
-- 设置隔离级别为读已提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 隔离级别有以下四种:
-- read uncommitted 读未提交
-- read committed 读已提交
-- repeatable read 可重复读
-- serializable 串行化
脏读,一个事务读取到了另一个事务未提交的数据
脏读演示:
set global transaction isolation level read uncommitted;
select @@transaction_isolation;
start transaction;
select * from account;
start transaction;
update account set money = money - 500 where name = 'tom';
update account set money = money + 500 where name = 'jerry';
select * from account;
rollback;
select * from account;
脏读解决方案:
将全局的隔离级别提升为读已提交:read committed
set global transaction isolation level read committed;
6.窗口A提交事务之后,窗口B再次查询账户信息,查询到提交之后的数据
不可重复读,就是在一个事务中,进行查询操作,每次查询到的数据都不一致
不可重复读演示:
不可重复读解决方案
将全局的隔离级别升为:repeatable read
set global transaction isolation level repeatable read;
update account set money = money - 500 where name = 'tom';
commit;
同一事务中,为了保证对此查询的数据一致,必须使用 repeatable read 隔离级别
幻读,查询某条数据发现不存在,然后准备插入该条数据,结果发现该记录已存在,无法插入,就发生了幻读。
幻读演示:
start transaction;
select * from account where id = 3;
-- 插入
insert into account values(3,'lucy',1000);
-- 提交事务
commit;
窗口A查询没查到,插入为什么不可以,还报错说主键重复,为什么?
幻读解决方案
将全局的隔离级别升为:serializable
set global transaction isolation level serializable;
select * from account where id = 4;
insert into account values(4,'jack',1000);
insert into account values(4,'jack',1000);
-- 提交事务
commit;
执行成功,未出现幻读
注意:
serializable串行化可以彻底解决幻读,但是事务只能排队执行,严重影响效率。