【MySql专栏】—— 事务的特性

一、事务的概念

事务是一个或几个操作组成的一个整体执行单元,它们要么全部执行,要么全不执行,不能只执行其中的某几个操作;可以理解为一个事务是一个程序中执行的最小单元,在同一个事务中的多条sql语句,要么全部成功,要么全部失败。

在MySql中事务的实现是在引擎层,而MyISAM引擎不支持事务,InnoDB支持事务。

二、事务的特性

事务包含四个特性:原子性、一致性、隔离性、持久性,简称ACID性。

1.原子性:事务是应用中最小的执行单位,就像自然界中原子是最小的颗粒一样,具有不可分隔的特性,事务是应用程序中不可分隔的最小逻辑执行单元。

2.一致性:事务执行的结果,必须使数据库从一种一致状态,变成另为一种一致状态。例如事务中包含两条需要执行的sql语句,要么两条语句都执行成功,要么都不成功,这就是一致性。

3.隔离性:各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。即,并发执行的事务之间是不会互相干扰的。

4.持续性:指事务一旦提交,对数据的修改就是永久的,不可逆的。

三、事务的隔离级别

事务的隔离级别也是包含了四种,分别是读未提交(read uncommited)、读提交(read commited)、可重复读(repeatable read)和串行化(serializable)。

1.读未提交:一个事务还没有提交时,它做的改变就能被别的事务看到。

2.读提交:一个事务提交之后,它做的改变才能被别的事务看到。

3.可重复读:一个事务执行过程中看到的数据,总是和这个事务启动时看到的数据时一致的,并且当前事务的修改,只有在提交之后,才能被别的事务看到。

4.串行化:对操作的某行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后一个事务只有等到前一个事务执行完成后,才能继续执行。

事务的隔离级别从低到高是读未提交 —> 读提交 —> 可重复读 —> 串行化。因为事务的隔离级别越高,性能就越低,所以性能从低到高就是 串行化 —> 可重复读 —> 读提交 —> 读未提交。

下面举例来说明下,在不同事务的隔离级别下,所执行的结果会是怎么样的呢?

首先来建立一个表,并插入一条数据:

create table T(id int) engine=InnoDB;
insert into T values(1);
事务A事务B
启动事务,得到查询值为1启动事务
 得到查询的值为1
 将1变为2
再次查询得到数值V1 
 提交事务
查询得到数值V2 
提交事务 
查询得到数值V3 

那么我们来看一下在四种隔离级别下,查询到的这个值会是什么?

1.在“读未提交”下:事务B做的修改,在事务还没有提交情况下,事务A已经可以得到数值,所以V1,V2,V3都是2.

2.在“读提交”下:事务B做的修改,在未提交情况下,事务A是得不到其修改的值,所以V1还是1,只有等到提交后,才可以获得修改后的值,所以V2,V3为2。

3.在“可重复读”下:事务A查询到的值,在整个事务中总是和这个事务启动时看到的数据时一致的,所以V1,V2为1,当事务提交后V3为2。

3.在“串行化”下:事务A在操作时,加了“读”锁,那么事务B做的将1变为2,需要"写"锁,读写冲突,需要等待,这时V1,V2的值为1,等事务A提交事务后,将1变为2继续执行,V3查到的值为2。

四、事务的隔离实现和事务的启动方式

1.事务的隔离实现

当我们在更新数据库的信息的时候,都会记录一条回滚操作,当前的最新值,通过回滚操作就可以得到前一个值。这里会用到MySql中很重要的一个特性就是多版本并发控制(MVCC),就是某一条记录,会存在多个版本,不同时刻启动的事务,就会得到不同的版本,通过对应版本的回滚操作就可以得到当前值的前几个版本值。

当然我们的回滚日志也不是一直存在的,MySql会自己判断,当没有事务需要这些回滚日志时,回滚日志就会被删除;简单理解为,当没有事务操作某行数据时,和这行数据相关的回滚日志就会被删除。

InnoDB在“可重复读”隔离级别下,每次事务开启时,都会生成一个快照文件,来获得事务开启之前的全部数据,事务启动的时候,系统会默认分配一个事务id,并且这个事务id是自增的,上面说过,一条数据对应多个数据版本,当某个事务修改这条数据的时候,会把事务id传给数据版本i(row_id),当我们启动一个事务的时候,InnoDB会为启动时所有的未提交的事务创建一个事务id的数组,数组里最小的事务id记为低水位,事务id最大值加1的记为高水位,根据这个数组合高低水位,可以创建一个一致性视图:

图片来源:https://time.geekbang.org/column/article/70562

根据这个视图和数据版本的(row_id)来比较存在三种可能:

1.在绿色区域,表示数据是在开启当前事务提交的数据,则这个数据在当前事务中是可见的;

2.在红色区域,表示数据是在开启当前事务之后提交的数据,则这个数据对当前事务是不可见的;

3,.在黄色区域,如数据版本(row_id)是事务id数组中的某个值,表示这个数据是未提交数据,不可见,如果不是事务id数组中的某个值,表示数据已提交是可见的。

举例说明,如下有三个事务,其操作流程如下:

事务A事务B事务C

start transaction with

consistent snapshot(开启一个事务)

  
 

start transaction with

consistent snapshot(开启一个事务)

 
  update T set k = k+1 where id = 1
 

update T set k = k+1 where id = 1;

select * from T where id = 1;

 

select * from T where id = 1;

commit

  
 commit; 

假设一开始k的值为1,数据版本为(1,90)事务A启动之前只有一个未提交的事务,事务id为95,事务A,B,C的事务id分别为100,101,102,当事务C执行完更新后,数据版本就变成(2,102),当执行事务B的时候就变成了(3,102),所以当事务A进行查询的时候先查到(3,101),101大于100,处于红色区域,数据不可见,往上查询版本读到数据(3,102),102大于100还是不可见,继续查询(1,90),90小于95,数据可见,所以查询结果为1。

2.事务的启动方式

事务可以显式启动事务语句,在执行语句钱加begin或start transaction,提交事务为commit,回滚事务为rollback。将参数set autocommit = 0时,意思为将线程提交自动关闭,只要执行select语句,事务自动启动,但不会自动提交,只有当执行commit和rollback时,结束事务。一般将set autocommit设置为1,通过显示语句来开启和提交事务。

这篇文章就到这里,感兴趣的小伙伴,可关注本专栏,你的关注就是我更新的最大动力MySql专栏

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值