一、简介:
MYSQL中,事务其实是一个最小的不可分割的工作单元,事务能够保证一个业务的完整性。
比如我们的银行转账:
a --> -100
update user set money=money-100 where name='a';
b--> +100
update user set money=money+100 where name='b';
-- 实际的程序中,如果只有一条语句执行成功了,而另外一条没有执行成功?
-- 出现数据前后不一致。
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
-- 多条sql语句,可能会有要么同时成功、要么同时失败的要求。
-- mysql中如何控制事务?
二、创建例子
mysql 默认是开启事务的(自动提交)。
默认开启事务的作用是什么?
--当我们取执行一个sql语句的时候,效果会立即体现出来,不会回滚。
create database bank;
create table user(
id int primary key,
name varchar(20),
money int
);
insert into user values(1,'a',1000); # 不能回滚
-- 事务回滚:撤销sql语句执行效果,以下语句执行失败
rollback;
-- 设置mysql事务自动提交为 false
set autocommit=0;
-- 上面的操作,关闭了mysql的自动提交(commit)
insert into user values(2,'b',1000);
rollback; # 回滚一下,零时数据可以撤销
select * from user; # 表格回到原始状态,第二条insert语句没有提交
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
insert into user values(2,'b',1000); # 再一次插入数据
commit; # 手动提交
rollback; # 再次撤销将会失败(持久性)
select * from user; # 表格回到原始状态,第二条insert语句没有提交
-- 自动提交? @@autocommit=1;
-- 手动提交? commit;
-- 事务回滚? rollback;
-- 如果这个时候转账:
-- 事务给我们提供了一个反悔的机会。
set autocommit=1; # 设置回自动提交
select @@autocommit; # 显示autocommit状态
begin;
-- 或者
start transaction;
-- 都可以帮我们手动开启一个事务
-- 手动开启事务(1)
begin;
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
rollback; # 成功
-- 手动开启事务(2)
start transaction;
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
rollback; # 成功
-- 手动提交事务(3)
start transaction;
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
commit; # 事务开启之后,一旦committ提交后,就不能回滚了
rollback; # 失败
三、事物的四大特征ACID:
A 原子性:事务最小的单位,不可以再分割。
C 一致性:事务要求,同一事务中的sql语句,必须保证同时成功或同事失败。
I 隔离性:事务1 和 事务2之间是具有隔离性的。
D 持久性:事务一旦结束(commit, rollback),就不可返回。
事务开启:
set autocommit=0; # 修改默认提交
begin;
start transaction;
事务手动提交:
commit;
事务手动回滚:
rollback;
事务的隔离属性:
read uncommitted; # 读未提交的
read commited; # 读已经提交的
repeatable read; # 可以重复读
serializable; #串行化
1、read uncommitted
如果有 事务a 和 事务b,
a 事务对数据进行操作,再操作的过程中,事务没有被提交,但是 b 可以看见 a 的操作结果。
一个事务可以读取另一个未提交事务的数据。
bank数据库 user库
insert into user values(3, '小明', 1000);
insert into user values(4,'淘宝店', 1000);
–如何查看数据库的隔离级别?
mysql 8.0
-- 系统级别的
select @@global.transaction_isolation;
-- 会话级别的
select @@transaction_isolation;
– mysql 的默认隔离级别为 REPEATABLE-READ
–如何修改隔离级别?
set global transaction isolation level read uncommitted; # 将系统隔离级别设置为uncommitted
举例:小明买鞋时间
– 转账买鞋子:800块钱
– 小明 -> 成都 ATM
– 淘宝店 -> 广州 ATM
start transaction;
update user set money=money-800 where name='小明';
update user set money=money+800 where name='淘宝店';
– 给淘宝店打电话,说你去查一下,是不是已经到帐了
– 淘宝店在网上发货
–晚上请女朋友请朋友吃好吃的-1800
– 小明在成都rollback
rollback;
– 结账的时候发现钱不够
select * from user;
– 如果两个不同的地方,都在进行操作,如果事务a开启后,他的数据可以被其他事务读取到。
– 这样就会出现(脏读):一个事务读到另一个事务没有提交的数据,就叫做脏读。
– 实际开发是不允许脏读的: 在系统 uncommitted的情况下。
2、read committed:读已经提交的
就是一个事务要等另一个事务提交后才能读取数据。
set global transaction isolation level read committed;
select @@global.transaction_isolation;
举例:报表事件
–bank 数据库 user 表
–小张:银行会计
start transaction;
select * from user;
–小张出去上厕所了,抽烟
–小王:
start transaction;
insert into user values(5,'c',100);
commit;
–小张上完厕所,抽完烟回来了
select avg(money) from user;
–money的平均值不是1000,变少了
–虽然我能读到你提交的数据,但还是会出现问题(由于数据更新导致的,不能重复读取相同的数据。)
–不可重复读现象: read committed
3、repeatable read; # 可以重复读
set global transaction isolation level repeatable read;
select @@global.transaction_isolation;
–在REPEATABLE_READ 隔离级别下又会出现什么问题?
举例:
select * from user;
–张全蛋-成都
start transaction;
–王尼玛-北京
start transaction;
–张全蛋开始插入6号卡,并且自己能看到
insert into user values(6,'d',1000);
–王尼玛看不到6卡,他也试图插入
insert into user values(6,'d',1000)
–因此在王尼玛插入数据时会出现,6卡已经被插入过了
EEROR 1062(23000): Duplicate entry '6' for key 'PRIMARY'
–这种情况叫做幻读!
–事务a操作和事务b同事操作一张表,事务a提交的数据也不能被事务b读到,就可能造成幻读。
4、serializable:串行化
set global transaction isolation level repeatable read;
select @@global.transaction_isolation;
-- 修改隔离级别为串行化
–张全蛋-成都
start transaction;
–王尼玛-北京
start transaction;
–张全蛋
insert into user values(7,'赵铁柱',1000);
commit; # 已经提交了
–王尼玛
select * from user;
–张全蛋
start transaction;
insert into user values(7,'王小花',1000); # 会显示插入无法执行
insert into user values(8,'王小花',1000);
-- sql语句被卡住了,语句无法执行
-- 当user 表被另外一个事务操作的时候,其他事务里面的写操作,是不可以进行的。
-- 进入派对状态(串行化),直到王尼玛那边事务结束之后,张全蛋的写入操作才会执行。
–王尼玛
commit;
–张全蛋
-- 王尼玛commit之后,才能执行成功插入
insert into user values(8,'王小花',1000); # Query OK
–串行化带来的问题是,性能较差!!!
READ-UNCOMMITTED > TEAD-COMMITTED > REPEATABLE-READ > SERIALIZABLE
–隔离级别越高,性能越差
mysql 默认级别是REPEATABLE-READ