MySQL如果没有事务,究竟会发生什么?

MySQL如果没有事务,究竟会发生什么?

一、从转账到事务解说

1、事务的概念:

我们先说个小故事:

平行世界里,某一对异地情侣boy和girl,boy有100块钱,girl有10块钱,现在boy想在给girl 手机转账50块钱,对应的数据库操作为:

UPDATE account SET balance = balance - 50 WHERE id = boy; # 1

UPDATE account SET balance = balance + 50 WHERE id = girl; # 2

只有操作1和2都共同完成的时候,boy的转账才算成功,可是,如何在执行了操作1之后,服务器突然断电了,操作2还没有执行。。。

这种情况就相当于从boy的账户里减了钱,但是girl的账户却没收到钱,这对情侣的总资金就变少了50,在他们看来那50块钱就像是不翼而飞了一样。

那数据库是怎么解决这种问题的呢?这就是我们即将要讲到的事务了。

“事务"不是某种客观存在的实体,而是一种逻辑操作的组合,这种组合的操作需符合ACID特性。

(1)、原子性(Atomicity) A

原子性取义于物理原子,原子是物质的最小单位,不可分割;原子性操作要么成功进入下一个状态,要么失败退回最初状态,不存在中间状态。

在我们现实生活中的转账,只有两种情况:要么转账成功,要么转账失败;无论是这里面的哪种情况转账双方的总资金是不变的;不存在上面提到平行世界中的那个例子——只转到一半。

很多在用户看来是单一操作的,在机器或者服务器看来就是很多条操作的组合(就像高级语句int i =0;对应的多条的二进制语句一样),原子性就是保证这些组合要么全执行,要么全不执行(退回执行前的状态)。

(2)、一致性(Consistency) C

这里的一致性是指数据库世界和现实世界规则的一致性,当然数据库里的规则当然没能和现实世界一一对应,毕竟现实世界的规则很多很多,比如身份证号不能重复,性别只有男和女,房价不能为负,高考分数最高是750等等。

也正因为这个的存在,数据库层面也出现了各种约束:主键约束、唯一约束、非空约束等。

一致性,比如一个高考生的成绩表中出现了751分,这种情况不用去核查都知道是数据错了,因为现实世界中高考成绩是不可能超过750的(最起码现在是这样)

(3)、隔离性(Isolation) I

假设前面提到的boy给girl转50块,分两次操作:第一次(T1)转30,第二次(T2)转20;但每一次转账都细分为6个步骤。

一般情况下,无论是先进行T1,还是T2,boy都可以成功转账50给girl的,毕竟30+20=50,20+30=50;但是实际上却没有这么简单,真实数据库中,T1和T2的操作可交替进行的(类比操作系统中的进程并发)。

经过上图的流程,最后我们惊讶地发现boy的账户由100变到了70,girl的账户由10变成了60;原来两人的总资金100+10=110,而现在70+60=130,现总资金比原总资金多了20。

这很明显就存在了问题,不然如果一直这样下去的话,所有人都去转账了,钱不少反而变多。

所以在数据库中,我们不仅要保持事务的原子性,还要隔离其他事务对某种事务的影响,使得其他的状态转换并不能影响该状态转换的结果,这就是隔离性。

(4)、持久性(Durability) D

持久性是指一旦状态转换成功后,该状态就被记录下来,无法回滚。

就好像boy给girl转30,一旦转成功之后,转账成功的状态就被永久记录下来了,不可逆,boy不可能单方面把30要回来的,当然如果是girl把30给回boy,这已经是另外一个过程了。

说了这么多,终于到了事务概念的总结了:

事务就是需要保持以上四种特性(ACID)的一系列数据库操作!

二、事务的状态及其转换

事务是一个抽象的概念,是一系列的数据库操作的集合,在它的执行过程中分为一下几种状态:

  1. 活动的:事务在正常执行。
  2. 部分提交的:事务操作已经完成,但是操作的结果只停留在内存中,还没有刷盘保存。
  3. 提交的:事务操作被刷盘之后。
  4. 失败的:只要事务无法刷新回磁盘都是失败的,数据库不允许不具有持久性的事务存在,又因为原子性,既然无法成功,那只能失败。
  5. 中止的:回滚,由中间状态回到初始状态。

在这里插入图片描述

三、MySQL中事务的基本操作

1、开启事务

BEGIN;

START TRANSACTION;

事务可以由以上两个语句开启,BEGIN和START TRANSACTION有相同的功效,都代表着一个事务的开启,但是START TRANSACTION后边可以加几个修饰符:

  1. READ ONLY ,只读,标志着当前开启的事务是一个只读事务,只能读取数据,不能修改其实事务也能访问到的数据;但是可以修改临时表中的数据,临时表只存在当前会话,其他事务无法访问。

    START TRANSACTION,READ ONLY ;

  2. READ WRITE ,读写,标志着当前开启的事务是一个读写事务,既可以读取数据,也可以修改数据。
    START TRANSACTION,READ WRITE ;

  3. WITH CONSISTENT SNAPSHOT ,启用一致性读。
    START TRANSACTION,WITH CONSISTENT SNAPSHOT;

若没显式指明事务的访问模式,则默认是READ WRITE,读写模式!

2、提交事务

BEGIN或START TRANSACTION开始的事务,一直到COMMIT结束。

COMMIT;

(1)自动提交

MySQL中有一个系统变量 autocommit ,默认是on开启的状态的,当我们不显式使用BEGIN或START TRANSACTION开始事务,则默认每一个语句都是一个事务,执行完毕后自动提交(受autocommit的状态影响)

(2)隐式提交

有些时候,在autocommit =off关闭的状态下,事务还是悄悄的被提交了,这种就是隐式提交。

下面列举几种常见的隐式提交的情况:

  1. 定义或修改数据库对象的数据定义语言(Data definition language,缩写为: DDL )。
    数据库对象是指数据库 、 表 、 视图 、 存储过程 等
    当我们使用CREATE 、ALTER 、 DROP 等DDl语句 去修改数据库对象的时候,这些语句就会被隐式提交(这时候是提交由事务开始到该语句的所有,并不是单单提交该语句;可以理解为这种情况下,该DDl语句等效与COMMIT)。

  2. 隐式使用或修改 mysql 数据库中的表
    ALTER USER 、 CREATE USER 、 DROP USER 、 GRANT 、 RENAME USER 、 REVOKE 、 SET PASSWORD 等

  3. 两个开启事务语句之间
    BEGIN;

    SELECT … # 事务中的一条语句
    UPDATE … # 事务中的一条语句
    … # 事务中的其它语句

    BEGIN; # 此语句会隐式的提交前边语句所属于的事务

3、手动中止事务

事务是具有ACID特性的一系列数据库操作,当我们发现其中某条语句写错了的时候,可以手动中止事务,让事务回滚(回到某一状态)。

ROLLBACK; # 回滚到上一次提交后的状态(初始状态)

在事务未提交之前,我们都可以使用ROLLBACK进行回滚,复原。

ROLLBACK是手动回滚的指令,但是在事务遇到错误而无法继续执行的时候,会自动回滚的。

在不支持事务的存储引擎中无法进行回滚。

保存点(savepoint):

当我们在一个事务中执行了很多语句,进行了很多操作,突然发现写错了一个,那这时候该怎么办?

使用ROLLBACK回滚吗?可是ROLLBACK只能回到上次事务提交的时候(初始状态),也就是说如果使用ROLLBACK的话,之前的操作全部被推翻,只能重到再来,有种“一夜回到解放前的感觉”。

为了解决这个问题,保存点(savepoint)就诞生了。

在事务开始之后,我们可以在确认前面的操作没问题的前提下,为当前位置设置一个保存点,这样就算后面出错了也能回到当前位置,而不是最初位置。

保存点定义语句:

SAVEPOINT 自定义保存点名称

回滚到某保存点的回滚语句:

ROLLBACK TO 自定义保存点

四、事务的隔离级别

事务隔离级别的划分源于事务隔离性和服务器性能之间的矛盾,由两者间的平衡力度不同而划分。

我们的服务器可以为多个事务提供服务,当多个事务都需要访问同一数据的时候,就需要体现隔离性了,是让它们排队进行呢(串行访问,性能差),还是怎样,这就涉及到了隔离级别。

1、数据并发产生的问题

串行访问,性能差,并发访问,由涉及到隔离性,两者间如何取舍呢?

为此,我们需要先知道数据并发时所可能产生的问题都有哪些,对结果的影响程度如何。

(1)脏写( Dirty Write )

事务A修改了事务B修改而未提交的数据,此时若事务B回滚的话,事务A就会看到一个奇怪的值(和它改的不一样

(2)脏读( Dirty Read )

脏读和脏写的情况有些类似,事务A查看了事务B修改而未提交的数据,若事务B回滚的话,事务A相当于读到一个不存在的数据,这就是脏读。

(3)不可重复读( Non-Repeatable Read )

不可重复读,顾名思义就是不能重复读取数据,因为重复读取的数据不一致,一次是一个样,另一次又是另外一个样。

强调的是在一个事务A开启的过程中,别的事务B可以更改事务A中的数据并进行修改提交,造成事务A前后查询结果不一致!!

**注意和脏读区别:**脏读是指可以访问到未提交的数据,不可重复读是指在事务A查询的时候,其他事务可以对它所查询的数据进行修改。

(4)幻读( Phantom )

事务A查询一次后,事务B增加了某些行并提交,当事务A再次查询时,发现了幻影记录(事务B增加的行)。

强调的是第二次读取比第一次的多!

2、SQL中的四种隔离级别

针对上述提到的并发产生的问题,根据严重性大小进行排序:

脏写 > 脏读 > 不可重复读 > 幻读

并发会引发上述问题,并发度越高,隔离性越低,当串行时,隔离性最高,但性能却最低。

在这里插入图片描述

为此,SQL标准中制定了四个隔离级别:

  1. READ UNCOMMITTED :读未提交, 事务A可以读到别的事务B未提交的执行结果(中间数据),因此,可能导致脏读、不可重复读、幻读 。
  2. READ COMMITTED :读已提交, 事务A只能读到别的事务B提交后的结果,可以避免产生脏读,但是不可重复读,幻读的问题仍然存在。
  3. REPEATABLE READ :可重复读, 通过一定的机制使得事务A在别的事务B修改前后读到的数据一致,即实现了多次读取,结果一致。(这个具体的机制,我们后面再慢慢聊,不着急~)
  4. SERIALIZABLE :可串行化 ,事务对同一数据的访问只能串行访问,只有先来的访问结束了,其他事务才可访问,这个隔离级别可以解决上述所有并发产生的问题,但是服务器的性能却极差。

在这里插入图片描述

因为脏写的问题是最严重的(事务修改了数据,实际上却没有修改),所以SQL中制定的四个隔离级别都解决了脏写问题,所以此处就不谈脏写了。

脏写的严重性体会:

某老板开了家网店可以销售价值100的物件,为了优惠顾客,最初价格是10,后来优惠期过了,他的管理员启动事务B想把价格改回100的,可却手抖写少了两个0,成了1,但现在事务B还没提交;此时,老板启动事务A发现显示的价格是1,因此他赶紧骂骂咧咧地来修改为100并提交,可是另一边他的管理员发现错误了,他执行ROLLBACK回滚。。

此时,若老板和管理员说:“你不用管了,我已经修改好了,就用这个价格吧”

然而此时的真实价格是1.并不是100,如果继续下去的话,那老板岂不是亏惨了??

五、MySQL中支持的四种隔离级别

MySQL中默认的隔离级别是REPEATABLE READ ,但是我们可以通过以下语句修改:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL SERIALIZABLE;` `\#或` `SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = 'SERIALIZABLE';

使用GLOBAL或SESSION的区别:

GLOBAL针对所有事务会话有效,SESSION只针对当前事务会话有效。

两者都不影响之前或当前的事务,只会影响后续事务。

是我们可以通过以下语句修改:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL SERIALIZABLE;` `\#或` `SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = 'SERIALIZABLE';

使用GLOBAL或SESSION的区别:

GLOBAL针对所有事务会话有效,SESSION只针对当前事务会话有效。

两者都不影响之前或当前的事务,只会影响后续事务。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱上bug的小姐姐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值