为了说明“什么是事务”,我觉得先从事务所针对的目的说起,会比较容易切入。
对于一个软件系统来说,需要相应的数据资源(比如,数据库,文件系统等)来保存系统状态,在对系统状态所依托的数据资源进行访问的时候,为了保证系统始终处于一个“正确”的状态[1], 我们就必须对这些访问操作进行一些必要的限定,以此来保证系统状态的完整性。
事务就是以可控的方式对数据资源进行访问的一组操作,为了保证事务执行前后数据资源所承载的系统状态始终处于“正确”状态,事务本身持有四个限定属性, 即原子性(Atomicity),一致性(Consistency),隔离性(Isolation)以及持久性(Durability),也就是常说的事务的ACID属性:
-
事务的原子性(Atomicity)
-
原子性要求事务所包含的全部操作是一个不可分割的整体,这些操作要么全部提交成功,要么只要其中一个操作失败,就全部“成仁”(“一颗老鼠屎搅坏一锅汤”好像形容这种情况比较贴切哦)。 如果把整个事务的操作比做“钢七连”,那我们的口号就得从“不抛弃,不放弃”改成“要么不抛弃,要么就全部放弃”了。
事务的一致性(Consistency)
-
一致性要求事务所包含的操作不能违反数据资源的一致性检查,数据资源在事务执行之前处于一个数据的一致性状态,那么,事务执行之后也需要依然保持数据间的一致性状态。 对于一个证券系统来说,如果顾客银行账户和证券账户资金总和为10万的话(银行账户初始8万,证券账户初始2万),从银行账户的8万转账5万到证券账户的事务操作结束之后, 银行账户会剩余3万,证券账户为7万,两个账户的总和依然是10万,如果事务操作结束后,整个数据状态不是这个样子,那么就说系统处于不一致状态,而使用事务其中一个目的就是为了避免这种不一致性状态的产生。
事务的隔离性(Isolation)[2]
-
事务的隔离性主要规定了各个事务之间相互影响的程度。隔离性概念主要面向对数据资源的并发访问(Concurrency),并兼顾影响事务的一致性。当两个事务或者更多事务同时访问同一数据资源的时候, 不同的隔离级别决定了各个事务对该数据资源访问的不同行为。
不出意外的话,我们可以为事务指定四种类型的隔离级别,隔离程度按照从弱到强分别为“Read Uncommitted”,“Read Committed”,“Repeatable Read”和“Serializable”:
-
Read Uncommitted. 最低的隔离级别,Read Uncommitted最直接的效果就是一个事务可以读取另一个事务并未提交的更新结果。
Read Uncommitted是以较低的隔离度来寻求较高的性能,其本身无法避免以下几个问题:
-
脏读(Dirty Read). 如果一个事务中对数据进行了更新,但事务还没有提交,另一个事务可以“看到”该事务没有提交的更新结果,这样造成的问题就是,如果第一个事务回滚,那么,第二个事务在此之前所“看到”的数据就是一笔脏数据。
-
不可重复读取(Non-Repeatable Read). 不可重复读取是指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的,所以,Read Uncommitted也无法避免不可重复读取的问题。
-
幻读(Phantom Read)[3]. 幻读是指同样一笔查询在整个事务过程中多次执行后,查询所得的结果集是不一样的。幻读针对的是多笔记录。在Read Uncommitted隔离级别下, 不管事务2的插入操作是否提交,事务1在插入操作之前和之后执行相同的查询,取得的结果集是不同的,所以,Read Uncommitted同样无法避免幻读的问题。
-
-
Read Committed. Read Committed通常是大部分数据库采用的默认隔离级别,它在Read Uncommitted隔离级别基础上所做的限定更进一步, 在该隔离级别下,一个事务的更新操作结果只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新后的结果。 所以,Read Committed可以避免Read Uncommitted隔离级别下存在的脏读问题, 但,无法避免不可重复读取和幻读的问题。
-
Repeatable Read. Repeatable Read隔离级别可以保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数据的更新提交与否。 Repeatable Read隔离级别避免了脏读和不可重复读取的问题,但无法避免幻读。
-
Serializable. 最为严格的隔离级别,所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别, 但同时也是性能最差的隔离级别,因为所有的事务在该隔离级别下都需要依次顺序执行,所以,并发度下降,吞吐量上不去,性能自然就下来了。 因为该隔离级别极大的影响系统性能,所以,很少场景会使用它。通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证了系统性能不会损失太大,也能够一定程度上保证数据的一致性。
-