深入浅出MySQL事务(一)事务隔离

深入浅出MySQL事务(一)事务隔离

一、为什么需要事务?

事务有什么作用?简单的说,事务是确保一组操作要么全部成功,要么全部失败

一个最经典的例子就是转账,假设A要给B转账,需要进行一下步骤

  1. 检查A的余额是否足够

  2. 减少A的余额

  3. 增加B的余额

如果由于意外,只执行了前两步,第3步没有执行成功,那会是怎样的结果

结果就是A的余额减少了,但是B的余额没有增加,我想这样的系统没有人敢使用

所以我们需要将这三个步骤使用一个事务来执行,要么全成功,要么全失败

二、隔离级别

事务有四个特性ACID(Atomicity:原子性;Consistency:一致性;Isolation:隔离性;Durability:持久性)

这里我们主要讲解一下事务的隔离性

当数据库上有多个事务同时执行的时候,就可能出现脏读、不可重复读、幻读的问题,为了解决这些问题,就有了隔离级别的概念。

隔离级别有四个级别

  • 读未提交(read uncommitted):一个事务还没提交,它做的更改其他就可以被其他事务看到
  • 读提交(read committed):一个事务提交后,它做的更改才可以被其他事务看到
  • 可重复读(repeatable read):一个事务启动后,总是无法看到其他事务的更改
  • 串行化 (serializable ):对于同一行记录,写会加写锁,读会加读锁,当事务之间读写冲突时,必须等待上一个事务执行完后才能继续执行,使得事务编程了串形执行

下面使用一个例子来说明这四种隔离界别

首先创建一张表并插入一个值

create table T(
	id int,
) engine=innodb;

insert into T(id) value(4);

以下面的顺序来执行两个事务

在这里插入图片描述

读未提交的隔离条件下

  • T1:事务A读到id等于4
  • T2:事务A读到id等于5
  • T3:事务A读到id等于5
  • T4:事务A读到id等于5

读提交的隔离条件下

  • T1:事务A读到id等于4
  • T2:因为事务B未提交,所以其更改对事务A不可见,事务A读到的id等于4
  • T3:由于事务B提交了,所以其更改对事务A可见,事务A读到的id等于5
  • T4:读到id等于5

可重复读的隔离条件下

  • T1:事务A读到id等于4
  • T2:由于可重复读情况下,事务无法见到其他事务的操作,所以事务A未提交前读到id都等于4
  • T3:事务A读到id等于4
  • T4:由于事务A提交了,所以已提交的事务对其是可见的,此时读到的id=5

串形化的隔离条件下

  • T1:事务A读取时,会给数据行加读锁,此时id等于4
  • T2:事务A为提交,还继续拥有读锁,事务B的操作会被阻塞,所以此时id等于4
  • T3:事务A为提交,还继续拥有读锁,事务B的操作会被阻塞,所以此时id等于4。之后提交事务后,释放读锁,事务B开始运行,修改数据的时候,为行加上写锁
  • T4:等待事务B执行完成后,开始执行,此时id等于5

可以通过启动参数 transaction-isolation 来设置事务的隔离级别,使用下面的语句可以查看当前的隔离级别

mysql> show variables like 'transaction_isolation';
 
+-----------------------+----------------+
 
| Variable_name | Value |
 
+-----------------------+----------------+
 
| transaction_isolation | READ-COMMITTED |
 
+-----------------------+----------------+

三、事务隔离的实现

在MySQL中,每条记录在更新的时候都会记录一条回滚操作,当前最新记录可以通过回滚操作得到上一状态的值

举个例子,如果将1依次改为2、3、4,那么在回滚日志中会有如下的记录

在这里插入图片描述

我们可以将当前值4,通过回滚日志的回滚操作,获取之前状态下的值。也就是将4通过回滚操作,我们知道了上一状态是3,再通过回滚操作可以知道上一状态是2

你看,这样我们同一条记录存在多个版本,当然只有最新版本的数据是存在的,而其他版本需要通过回滚操作来获取

事务隔离的实现是通过创建一个视图,根据视图的逻辑来判断当前哪个版本的数据对事务是可见的

可重复读的隔离级别举例,事务A启动的时后创建了视图A,事务B启动的时候创建了事务B,事务C启动的时候创建了视图C,如下所示

在这里插入图片描述

根据视图的逻辑判断,事务A看到的数据是1,事务B看到的数据是2,事务C看到的数据是4

如果将当前值修改为5,那就会变成这样

在这里插入图片描述

可以看到事务读到的数据依旧没有改变。同一记录数据可以存在多个版本,就是数据库的多版本并发控制(MVCC)

对于可重复读的隔离级别,这个视图在事务启动的时候就会创建一个视图,并且整个事务执行中都会使用这个视图,所以在事务中看到的数据都是一样的

对于读提交的隔离级别,在事务中每次执行sql操作都会创建一个新的视图

对于读未提交的隔离级别,总是读到最新的数据,因此没有视图的概念

对于串形化的隔离级别,直接通过加锁来避免并发访问,也没有视图的概念

需要注意的是,回滚日志并不是一直保留下来的,当它不需要被使用的时候就会被删除,那么什么时候不需要呢?就是没有比这个回滚日志更早的视图

因此避免使用长事务,因为长事务会产生很老的视图,这样会导致回滚日志无法被删除而大量占用存储空间

四、事务的启动方式

其实很多时候并不是有意使用长事务,通常是由于误用所致。MySQL的事务启动方式有以下几种

  1. 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback

  2. sett autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接

可以看到第二种方式如果在长连接的情况下,会不经意地启动了一个长事务,所以建议将使用set autocommit=1

五、小结

本文讲述了不同事务隔离级别的现象以及事务隔离的原理,同时也讲述了长事务带来的风险,希望通过本文,你可以更加正确地使用事务

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值