MySQL进阶(再论事务)——什么是事务 & 事务的隔离级别 & 结合MySQL案例详细分析

在这里插入图片描述

前言

MySQL最为最流行的开源数据库,其重要性不言而喻,也是大多数程序员接触的第一款数据库,深入认识和理解MySQL也比较重要。

本篇博客阐述MySQL的事务的定义和特性,原子性,一致性,隔离性,持久性;然后结合实际的案例详细分析了事务的隔离级别:序列化(SERIALIZABLE)、可重复读(REPEATABLE READ)、提交读(READ COMMITTED)、未提交读(READ UNCOMMITTED)。

本系列文章合集如下:

【合集】MySQL的入门进阶强化——从 普通人 到 超级赛亚人 的 华丽转身

在这里插入图片描述

引出


1.事务(TRANSACTION)是一个不可分割的逻辑单元,包含了一组数据库操作命令,并且把所有的命令作为一个整体向系统提交,要么都执行、要么都不执行;

2.隔离级别和脏读、不可重复读以及幻象读的对应关系如下:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED允许允许允许
READ COMMITED不允许允许允许
REPEATABLE READ 【默认的隔离级别】不允许不允许允许
SERIALIZABLE不允许不允许不允许

3.在MySQL数据库中,默认的事务隔离级别是REPEATABLE READ 可重复读;

一、什么是事务

事务(TRANSACTION)是一个不可分割的逻辑单元,包含了一组数据库操作命令,并且把所有的命令作为一个整体向系统提交,要么都执行、要么都不执行。

二、事务的特性

事务必须具备以下四个属性,简称ACID 属性

1、原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。
比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。

2、一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

换一种方式理解就是:事务按照预期生效,数据的状态是预期的状态。
比如账户A和账户B两者的余额加起来总共是5000,那么不管A和B之间如何转账,转账几次,事务结束后两个账户的钱相加起来最终还是5000。

3、隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间,要相互隔离。
在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同,分别是:未授权读取,授权读取,可重复读取和串行化。

4、持久性(durability)
一旦事务提交,那么数据的状态就会被永久的保存到数据库中。
即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束的状态。

三、事务的使用步骤

1、开始事务

BEGIN ;START TRANSACTION;

2、提交事务

COMMIT ;

3、回滚(撤销)事务

ROLLBACK ;

五、SQL实战

使用事务有两种方式,分别为显式事务隐式事务

1、显式事务

(1)创建account表,并插入测试数据

在这里插入图片描述

create table account(
    id int primary key auto_increment,
    name varchar(20) unique not null,
    balance int
);

insert into account values(null,'zs',1000);
insert into account values(null,'lisi',1);

在这里插入图片描述

commit之后数据才提交

在这里插入图片描述

事物回滚,删除一条数据进行测试

在这里插入图片描述

回滚

在这里插入图片描述

BEGIN;
delete from account where name='zs';

ROLLBACK

保存点SAVEPOINT

在事务中创建保存点,方便后续针对保存点进行回滚。一个事务中可以存在多个保存点。

在这里插入图片描述

回滚到指定位置

在这里插入图片描述

begin;
delete from account where name='zs';
savepoint first;
delete from account where name='lisi';

ROLLBACK TO first;
COMMIT

2、隐式事物

DROP TABLE account;
create table account(
    id int primary key auto_increment,
    name varchar(20) unique not null,
    balance int
);

insert into account values(null,'zs',1000);
insert into account values(null,'lisi',1);

(1)在MySQL中执行DML语句,会自动提交事物。

delete from account where name='zs';

在这里插入图片描述

(2)MySQL中有一个系统变量 autocommit, 可以查看是否开启自动提交事物。

SHOW VARIABLES LIKE 'autocommit';

(3)把系统变量autocommit 的值设置为 OFF ,则会关闭自动提交。

SET autocommit = OFF;
#或
SET autocommit = 0;

在这里插入图片描述

(4)再次执行DML语句,发现不会自动提交。

insert into account values(null,'zs',1000);

在这里插入图片描述

(5)此时必须显示的提交事物才能生效

commit;

在这里插入图片描述

六、事务的隔离级别

1、理论

MySQL 中事务的隔离级别一共分为四种,分别如下:

  • 序列化(SERIALIZABLE)
  • 可重复读(REPEATABLE READ)
  • 提交读(READ COMMITTED)
  • 未提交读(READ UNCOMMITTED)
时刻事务1事务2
T0读取商品初始库存为2
T1扣减库存,库存为1
T2读取商品库存为1
T3回滚事务,库存为2
T4扣减库存,提交事务
T5查询库存为1

在MySQL数据库中,默认的事务隔离级别是REPEATABLE READ 可重复读

2、查看事务的隔离级别

通过如下 SQL 可以查看数据库实例默认的全局隔离级别和当前 session 的隔离级别。

(1)MySQL8 之前使用如下命令查看 MySQL 隔离级别:

SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

(2)MySQL8 开始,通过如下命令查看 MySQL 默认隔离级别

SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

在这里插入图片描述

(3)通过如下命令可以修改隔离级别(建议修改当前 session 隔离级别即可,不用修改全局的隔离级别):

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

上面这条 SQL 表示将当前 session 的数据库隔离级别设置为 READ UNCOMMITTED,设置成功后,再次查询隔离级别,发现当前 session 的隔离级别已经变了。

在这里插入图片描述

注意,如果只是修改了当前 session 的隔离级别,则换一个 session 之后,隔离级别又会恢复到默认的隔离级别,所以我们测试时,修改当前 session 的隔离级别即可。

七、READ UNCOMMITTED 未提交读

该事物隔离级别会导致出现脏读、不可重复读、幻读。

1、准备测试数据

DROP TABLE account;
create table account(
    id int primary key auto_increment,
    name varchar(20) unique not null,
    balance int
);

insert into account values(null,'zs',1000);
insert into account values(null,'lisi',1);

在这里插入图片描述

2、脏读

一个事务读到另外一个事务还没有提交的数据,称之为脏读。具体操作如下

会话A

START TRANSACTION;
UPDATE account set balance = balance -1 WHERE name = 'lisi'

会话B

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

SELECT * FROM account

在这里插入图片描述

可以看到,A 窗口中的事务,虽然还未提交,但是 B 窗口中已经可以查询到数据的相关变化了。

这就是脏读问题。

3、不可重复读

不可重复读是指一个事务先后读取同一条记录,但两次读取的数据结果不同,称之为不可重复读。

在这里插入图片描述

--  会话A
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT * FROM account WHERE name ='lisi';
SELECT SLEEP(10);
SELECT * FROM account WHERE name ='lisi';
COMMIT;
--  会话B
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

START TRANSACTION;
UPDATE account SET balance =  balance -500 WHERE name ='lisi';
COMMIT;

lisi的账户已经发生了变化,即前后两次查看 lisi账户,结果不一致,这就是不可重复读

和脏读的区别在于,脏读是看到了其他事务未提交的数据,而不可重复读是看到了其他事务已经提交的数据

(由于当前 SQL 也是在事务中,因此有可能并不想看到其他事务已经提交的数据)。

4、幻读

是指当事务不是独立执行时发生的一种现象。

幻象读和不可重复读非常像,看名字就是产生幻觉了。

在这里插入图片描述

--  会话A
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT * FROM account;
SELECT SLEEP(10);
SELECT * FROM account;
COMMIT;
--  会话B
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

START TRANSACTION;
insert into account values(null,'wangwu',1);
COMMIT;

幻读

  • 幻读说的是存不存在的问题:原来不存在的,现在存在了,则是幻读
  • 不可重复读说的是变没变化的问题:原来是A,现在却变为了B,则为不可重复读

八、 READ COMMITTED 提交读

和 READ UNCOMMITTED 相比,READ COMMITTED 主要解决了脏读的问题,对于不可重复读和幻象读则未解决。

将事务的隔离级别改为 READ COMMITTED 之后,重复上面关于脏读案例的测试,发现已经不存在脏读问题了;重复上面关于不可重复读案例的测试,发现不可重复读问题依然存在。

1、脏读测试–解决

在这里插入图片描述

--  会话A
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
UPDATE account set balance = balance -1 WHERE name = 'lisi'
--  会话B
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

SELECT * FROM account

2、不可重复度和幻读–未解决

九、REPEATABLE READ 可重复读【默认】

和 READ COMMITTED 相比,REPEATABLE READ 进一步解决了不可重复读的问题,但是幻象读则未解决。

1、不可重复读测试–解决

在这里插入图片描述

--  会话A
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM account WHERE name ='lisi';
SELECT SLEEP(10);
SELECT * FROM account WHERE name ='lisi';
COMMIT;
--  会话B
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

START TRANSACTION;
UPDATE account SET balance =  balance +500 WHERE name ='lisi';
COMMIT;

注意,REPEATABLE READ 也是 InnoDB 引擎的默认数据库事务隔离级别

2、幻读–未解决

十、SERIALIZABLE

SERIALIZABLE 提供了事务之间最大限度的隔离,在这种隔离级别中,事务一个接一个顺序的执行,不会发生脏读、不可重复读以及幻象读问题,最安全。

如果设置当前事务隔离级别为 SERIALIZABLE,那么此时开启其他事务时,就会阻塞,必须等当前事务提交了,其他事务才能开启成功,因此前面的脏读、不可重复读以及幻象读问题这里都不会发生。


总结

隔离级别和脏读、不可重复读以及幻象读的对应关系如下:

隔离级别脏读不可重复读幻读
READ UNCOMMITTED允许允许允许
READ COMMITED不允许允许允许
REPEATABLE READ 【默认的隔离级别】不允许不允许允许
SERIALIZABLE不允许不允许不允许
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arya's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值