MySQL事务管理

1.事务的概念和应用

1.1概念

MySQL中,事务是一组原子性的SQL查询(语句),或者说是一个独立的不可分割的最小工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为某种原因执行失败,那么该组中的所有语句都不会执行。

简单来说,事物中的语句要么全部执行成功,要么全部执行失败

1.2MySQL中事务的自动提交机制

MySQL中默认采用自动提交(AUTOCOMMIT)的方式。也就是说,如果不是显示地开始一个事务,则每一条语句都会被当作一个事务执行提交操作。可以通过设置AUTOCOMMIT变量启用或禁用自动提交。

show variables like 'autocommit';

set autocommit = 1;#启用自动提交
set autocommit = 0;#禁用自动提交

1或ON表示启用,0或OFF表示禁用。

禁用后,所有的语句都被看作在一个事务中,直到显式的执行COMMIT提交或ROLLBACK回滚,该事务结束,同时开启一个新事务。

1.3使用方式

MySQL中可以使用START TRANSACTION语句开启一个事务,然后使用COMMIT提交事务,将事务修改的数据持久保留,或者使用ROLLBACK撤销所有修改。

例如用户1向用户2转账100元,分为三个步骤:

1.查询用户1的账户余额是否大于100

2.将用户1的账户余额扣减100

3.将用户2的账户余额增加100

1 start transaction ;
2 select money from account where user_id=1 ;
3 update account set money=money-100 where user_id=1 ;
4 update account set money=money+100 where user_id=2 ;
5 commit ;

如果2、3、4语句中有任何一条语句执行失败,那么这三条语句都不会执行,表中的数据不会发生改变。只有三条语句全部执行成功,表中用户1、用户2的余额才会发生改变。

2.事务的特性(ACID)

说到事务,就不得不提事务的四大特性(ACID):原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)。

2.1原子性(atomicity)

一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。

我们可以将一个事务类似的比喻为一个串联的电路,电路中有若干个串联的开关,在电路的最后接一个灯泡。那么电路中的每一个开关就可看成事务中的每一条语句,开关闭合代表语句执行成功,开关打开则代表执行世博啊,灯泡的亮灭就代表事务的提交与回滚。我们知道在串联电路中,只有当所有开关都处于闭合状态时,灯泡才能亮,即所有的语句都执行成功,事务才会提交。

2.2一致性(consistency)

数据库总是从一个一致性的状态转移到另一个一致性的状态。

就上面转账的例子来说,转账的100元不会凭空消失,而是从一个账户转移到另一个账户,账户1中的余额减少多少,账户2中的余额就会增加多少,数据的状态始终是保持一致的。即使在执行2语句后,执行3语句时系统崩溃,账户1也不会损失100元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。

2.3隔离性(isolation)

通常来说,一个事务所做的修改在事务提交前,对其他事务是不可见的。也就是事务提交前,其他事务并不知道该事务会对数据作何更改。

在上面的例子中,假设转账前用户1的余额为200元,在执行完2、3、4语句后且事务并未提交的同时,有另一个事务要查询用户1的余额,那么这个事务查询出来的余额依然是200元,而不是100元。

2.4持久性(durability)

一旦事务提交,则其所作的修改就会永久的保存到数据库中,或者可以说持久地保存到磁盘中。即使此时系统崩溃,修改的数据也不会丢失。

3.隔离级别

在SQL标准中定义了四种隔离级别,每种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。在较低级别的隔离下,事务之间的相互影响较少,因此可以允许更多的事务并发执行,系统的开销也越低。

四种隔离级别分别为:READ UNCOMMITTED(未提交读)、 READ COMMITTED(提交读) 、REPEATABLE READ (可重复读)、SERIALIZABLE(可串行化)。

3.1READ UNCOMMITTED(未提交读)

未提交读级别下,事务的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。

再以上面的转账为例,在事务A执行完语句2后,执行语句3之前。有另一个事务B要查询用户1的余额,那么此时该语句查询出来的余额就是事务A中语句2修改之后的数据(即100元),即使此时事务A并未提交。假如后续事务A在执行语句3时出错,事务A中所做的修改会全部回滚,此时,数据库中保存的用户1的余额仍为200元,那么事务B读取的用户1的余额数据就是无效的,甚至会导致数据的不一致性,这就是脏读。

这个级别会导致很多问题,从性能上来说,未提交读不会比其他级别好太多,但缺乏其他级别的很多好处,除非真的非常必要,在实际应用中很少使用。

3.2READ COMMITTED(提交读)

大多数数据库系统的默认隔离级别都是提交读(MySQL不是)。提交读满足前面提到的隔离性的简单定义:当一个事务开始时,只能“看见”已经提交的事务所做的修改。也就是说,一个事务从开始到提交之前,所做的修改对其他事务都是不可见的。这个级别有时也被叫做不可重复读(NONREPEATABLE READ )。

在该隔离级别下,一个事务中两次读取相同的数据,可能会得到不一致的结果。即在两次读取之间有另一个事务对数据进行了修改,并且该事务成功提交。

在上面转账的例子中,假设在事务B中有语句1、语句2都来查询用户1的余额,同时事务A中的语句也在执行,在事务B中执行语句1时,事务A并未提交,因此语句1查询出的余额为200元。但在事务B执行语句2时,事务A已经提交,那么事务A中对数据的修改就生效了,此时语句2查询出的余额就是100。这样就出现了在同一个事务中两次相同的查询出现不一致的结果。

3.3REPEATABLE READ (可重复读)

可重复读解决了不可重复读的问题。该级别保证了在同一个事务中多次读取同样的记录的结果是一致的。

在该隔离级别下,一个事务在读取数据时,其他事务不能对这些数据进行修改和插入新数据,直到当前事务提交。这就确保了在同一事务中进行多次的读取不受其他并发事务的影响,保证了数据的一致性。

但是在理论上,可重复读隔离级别还是无法解决另一个问题:幻读(Phantom Read)。

幻读,就是当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入新的记录,当之前的事务再次读取该范围的记录,就会产生幻行(Phantom Row)。

3.4SERIALIZABLE(可串行化)

可串行化是最高的隔离级别。它强制事务串行执行,即在同一时刻只能执行一个事务,避免了前面说的幻读问题,SERIALIZABLE

会在读取的每一行数据上都加上锁,所以可能导致大量的超时和锁争用问题。

实际应用中也很少使用这个隔离级别,因为会使系统性能大幅降低(禁止了并发事务),只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

参考资料:《高性能MySQL(第3版)》 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ethan123.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值