事务的概念
事务是访问并可能更新各种数据项的一个程序执行单元
。
换句话说,一个或者多个数据库操作的集合,这个集合必须构成单一的逻辑工作单元
。
所谓单一的逻辑工作单元,举个经常用到的例子:银行的转账功能。
转账对于客户来说是一个单一的操作,但是在数据库层面则是多个操作的集合,那么这个集合就是单一的逻辑工作单元。
MySQL提出事务的这个概念,是因为规定了这些操作集合必须满足的条件:AICD原则。
即把需要满足AICD的的数据库操作集合,称之为事务
。
A(原子性,Atomicity)
即要么全做,要么全不做
。转账功能要么成功,要么失败,不存在中间态(A扣款成功,B接收失败)。
I(隔离性,Isolation)
并发事务的执行,相互之间不影响
。
即使保障每个事务的原子性,但是如果多个事务并发交错执行,仍然会导致结果不一致,如图:
两次转账并未串行执行,而是交替执行,导致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如何实现隔离级别的,再接下来的学习中在分享。