MySQL事务篇

作者: zhl 分类: MySQL 发布时间: 2023-05-13 16:14

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语句
如果在转账操作中没有异常信息,可以完成转账操作
否则,在每一条执行语句中,都是每一条事务的提交,不能保证转账操作的原子性和一致性

  1. 查看/设置事务的提交方式(方式一)
    select @@autocommit;    //如果为 1,自动提交,如果为 0,手动提交
    set @@autocommit = 0;   //将事务提交方式设置为手动提交
  2. 设置事务的提交方式(方式二)
    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:不存在以上现象,但效率较低。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注