MySQL事务篇
MySQL的事务是在InnoDB存储引擎中实现的。
事务是什么?
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
典型案例:银行转账(张三向李四转账1000)
开启事务
查询张三账户余额,
张三账户余额-1000,
李四账户余额+1000
提交事务
默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句,MySQL会立即隐式的提交事务。
事务的场景?
create database tran;
use tran;
create table account(
id int auto_increment primary key,
name varchar(10),
money int
);
insert into account (name,money) values ('张三',2000),('李四',2000);
转账操作
1.查询张三余额
2.将张三账户余额-1000
3.将李四账户余额+1000
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
重置操作
update account set money = 2000 where name = '张三' or name = '李四';
执行以上SQL语句
如果在转账操作中没有异常信息,可以完成转账操作
否则,在每一条执行语句中,都是每一条事务的提交,不能保证转账操作的原子性和一致性
- 查看/设置事务的提交方式(方式一)
select @@autocommit; //如果为 1,自动提交,如果为 0,手动提交 set @@autocommit = 0; //将事务提交方式设置为手动提交
- 设置事务的提交方式(方式二)
START TRANSACTION 或 BEGIN
如果在事务的执行的过程中,存在异常或报错,要回滚事务(rollback),不要提交
提交事务
commit;
回滚事务
rollback;
事务的四大特性
- 原子性(Atomicity): 事务是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(Isolation): 数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下执行
- 持久性(Durability): 事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
并发事务问题
脏读
一个事务读到另外一个事务还没有提交的数据就是脏读。
在事务A中执行完操作1,2后,还未提交事务,在事务B中执行select 后,可以读到事务A中没有提交的数据,这就称为脏读。
不可重复读
一个事务先后读取同一条记录,但两次读取的数据不同叫做不可重复读。
在事务A中查询id = 1 的信息,在事务B中对数据库中id = 1的数据修改并提交,
再在事务A中查询id = 1的信息,与第一次查询结果不一样,这就成为不可重复读。
幻读
一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了"幻影"。
在事务A中查询id = 1 的信息,没有对应的数据行信息,接着在事务B中插入一行 id = 1 的数据并提交,然后在事务A中插入数据,发现不能插入id = 1对应的数据(id是主键),但是在当前事务A中查询id = 1 对应的数据行仍然为空,这就称为幻读。
事务的隔离级别
--查看事务的隔离级别
SELECT @@TRANSACTION_ISOLATION;
--设置事务的隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE]
读未提交
设置隔离级别为 read uncommitted
开启一个客户端,设置隔离级别为read uncommitted ,开启事务A,进行第一次select
use tran;
set session transaction isolation level read uncommitted;
start transaction;
select * from account;
再开启另外客户端,开启事务B,更改account表中的信息
use tran;
start transaction;
update account set money = money - 1000 where name = '张三';
在事务A中再进行select
select * from account;
事务B没有提交,但在事务A中可以读到事务B没有提交的数据,这就是脏读
读已提交
设置隔离级别为 read committed
再次开启事务A
use tran;
set session transaction isolation level read committed;
start transaction;
select * from account;
再开启事务B
use tran;
start transaction;
update account set money = money - 1000 where name = '张三';
在事务A中再进行select
事务A没有读到事务B未提交的数据,这种隔离级别(read committed) 解决了脏读
提交事务B(commit),再在事务A中select 数据发生改变。
事务A没有提交,读到了事务B中提交后的数据,前后两次读到的数据不一样,这就是不可重复读。
可重复读
设置隔离级别为 repeatable read
提交事务A,再开启事务A
use tran;
set session transaction isolation level repeatable read;
start transaction;
select * from account where id = 3;
再开启事务B
use tran;
start transaction;
insert into account (id,name,money) values (3,'王五',2000);
commit;
再在事务A中添加数据
insert into account (id,name,money) values (3,'wangwu',2000);
在事务A中报错,因为添加了重复的 字段3,但是在事务A中再次查询,结果并没有发现有添加的字段信息,这就是幻读,通过提交事务A才能读到在事务B中添加的信息。
串行化
设置隔离级别为 serializable
开启事务A
use tran;
set session transaction isolation level serializable;
start transaction;
select * from account;
select * from account where id = 4;
开启事务B
use tran;
start transaction;
insert into account (id,name,money) values (4,'Jack',2000);
因为事务A未提交,所以事务B一直等待
在事务A中 添加数据,并提交
insert into account (id,name,money) values (4,'Jack',2000);
commit;
此时在事务B 中报错,添加失败
通过设置serializable(串行化)隔离级,可以解决幻读
总结:
- Read committed :存在 脏读、不可重读、幻读
- Read uncommited :存在 不可重复读、幻读
- Repeatable read(MySQL默认的隔离级别):可以解决部分幻读但仍存在部分幻读
- Serializable:不存在以上现象,但效率较低。