MySQL事务详解(一):并发问题与解决之道

事务的概念

事务是访问并可能更新各种数据项的一个程序执行单元
换句话说,一个或者多个数据库操作的集合,这个集合必须构成单一的逻辑工作单元
所谓单一的逻辑工作单元,举个经常用到的例子:银行的转账功能。
转账对于客户来说是一个单一的操作,但是在数据库层面则是多个操作的集合,那么这个集合就是单一的逻辑工作单元。

MySQL提出事务的这个概念,是因为规定了这些操作集合必须满足的条件:AICD原则。
把需要满足AICD的的数据库操作集合,称之为事务

A(原子性,Atomicity)

要么全做,要么全不做。转账功能要么成功,要么失败,不存在中间态(A扣款成功,B接收失败)。

I(隔离性,Isolation)

并发事务的执行,相互之间不影响
即使保障每个事务的原子性,但是如果多个事务并发交错执行,仍然会导致结果不一致,如图:

Isolation.png

两次转账并未串行执行,而是交替执行,导致A账户减少50,B账户却多出了100。

C(一致性,Consistency)

上述转账的例子就破坏了一致性。
所谓一致性,就是数据库所处的状态的必须符合现实世界的约束。例如转账就必须保障A、B账户的总额是一定的。

D(持久性,Durability)

在系统保证一个事务被正确执行后,最终的结果要被持久性的保存下来,无论之后发生什么事故都不会将这次事务的结果丢失

事务的状态

事务并非总能顺利执行成功,会存在各种情况影响事务的顺利推进,为了准备了解事务,应对不同的情况。
需要更准确定义事务成功完成的含义,为此定义了事务的不同的状态及其流转过程。如图:

活动的(active): 初始状态,数据库正在执行事务。
部分提交的(partially commited): 最后一条语句执行完后,但还没有刷新到磁盘。
失败的(failed): 在活动状态或者部分提交状态,因为系统错误、断电等原因无法执行,或者人为停止执行。
中止的(aborted): 事务执行失败,需要将已经对数据库造成的影响撤销,执行回滚操作,执行完后即为中止状态。
提交的(commited): 事务顺利执行完成,并成功刷新到磁盘。
可以发现,中止状态和提交状态才是事务的最终状态

并发问题

并发事务必须符合AICD原则,来保障数据的正确性。如果不满足AICD,并发执行访问相同的数据,将会产生脏写、脏读、不可重复读、幻读等一系列问题。

脏写

如果一个事务修改了另一个尚未提交的事务修改过的数据,即为脏写。如下图:

T2修改了T1还没提交的数据,将number更新为20,但是T1未提交,意味着可能会回滚,如果T1发生rollback,那么T2更新的值20也将不复存在。

脏读

如果一个事务读到了另一个未提交事务修改过的数据,即为脏读,如下图:

T1未提交意味着可能会回滚。当T2读到number值为20后,T1发生回滚,T2就读到了一个不存在的数据。

不可重复读

在一个事务内,对相同的数据在不同的时刻读取到的值却不同,即不可重复读。
与脏读的区别在于:前者读到的是其他事务提交后的数据,后者读到的是其他事务未提交的数据。 如下图:

在T2事务内,两次读取值分别是10与20,读到的都是TI更新提交后的值(隐式事务),对于T2来说发生了不可重复读。

幻读

如果一个事务以相同的查询条件在两个不同的时刻查询,在第二次查询中,查询到了其他事务插入的数据,即发生了幻读。
幻读强调的是数据插入时产生的数据不一致,区别与不可重复读对于相同数据的更新操作。 如下图:


T2在事务内,第二次查询读到了T1插入的no=50的数据,发生了幻读。

事务隔离级别

针对并发事务下的问题,MySQL根据问题的严重性程度,设立了不同的隔离级别来规避。
问题严重性程度:脏写>脏读>不可重复读>幻读

四种隔离级别:
未提交读: 允许读取未提交数据,级别最低。
已提交的:只允许读取已提交数据,但不要求可重复读。
可重复读:只允许读取已提交数据,并要求一个事务两次读取一个数据项期间,其他事务不允许更新数据。
可串行化:保证事务可串行化调度,级别最高。

隔离级别与问题发生的关系如下图:


如图可以发现脏写在每种隔离级别下都不允许发生。
可串行化可以解决所有的问题。但为什么不只使用可串行化,而提供其他的隔离级别呢?
当然是为了效率考虑,隔离级别越高,效率越低。所以只能舍弃一部分隔离性换取性能

MySQL如何实现隔离级别的,再接下来的学习中在分享。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容