第17章 有关事务的楔子

第17章 有关事务的楔子

本章内容

  • 认识事物本身
  • 初识事务家族成员

认识事务本身

要说明“什么是事务”,我觉得,先从事务存在的目的说起,会比较容易切入。

对于一个软件系统来说,需要相应的数据资源(比如,数据库、文件系统等)来保存系统状态。在对系统状态所依托的数据资源进行访问的时候,为了保证系统始终处于“正确”的状态,我们必须对这些访问操作进行一些必要的限定,以此来保证系统状态的完整性

**事务就是以可控的方式对数据资源进行访问的一组操作。**为了保证事务执行前后,数据资源所承载的系统状态始终处于“正确”状态,事务本身持有4个限定属性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),这也就是常说的事务的ACID属性。

1.原子性

原子性要求事务所包含的全部操作是一个不可分割的整体,**这些操作要么全部提交成功,要么只要其中一个操作失败,就全部失败。**如果把整个事务的操作比作“钢七连”,那么我们的口号就得从“不抛弃,不放弃”改成“要么不抛弃,要么就全部放弃”了。

2.一致性

一致性要求事务所包含的操作不能违反数据资源的一致性检查,数据资源在事务执行之前处于某个数据一致性状态,那么,事务执行之后也依然需要保持数据间的一致性状态。对于一个证券系统来说,如果顾客的银行账户和证券账户资金总和为10万元的话(银行账户初始8万元,证券账户初始2万元),从银行账户的8万元转账5万元到证券账户的事务操作结束之后,银行账户会剩余3万元,证券账户为7万元,两个账户的总和依然是10万元。如果事务操作结束后,整个数据状态不是这个样子,那么就说系统处于不一致状态。使用事务的其中一个目的,就是为了避免这种不一致性状态的产生。

3.隔离性

**事务的隔离性主要规定了各个事务之间相互影响的程度。**隔离性概念主要面向对数据资源的并发访问(Concurrency),并兼顾影响事务的一致性。当两个事务或者更多事务同时访问同一个数据资源的时候,不同的隔离级别决定了各个事务对该数据资源访问的不同行为。

如果不出意外,我们可以为事务指定4种类型的隔离级别,隔离程度从弱到强分别为ReadUncommitted、ReadCommitted、RepeatableRead和Serializable。

  • Read Uncommitted。它是最低的隔离级别。ReadUncommitted最直接的效果就是,**一个事务可以读取另一个事务没有提交的更新结果。**Read Uncommitted是以较低的隔离度来寻求较高的性能,其本身无法避免以下几个问题。

    脏读(Dirty Read)。如果一个事务对数据进行了更新,但事务还没有提交,另一个事务就可以"看到”该事务没有提交的更新结果。这样造成的问题就是,如果第一个事务回滚,那么第二个事务在此之前所“看到”的数据就是一笔脏数据。

    不可重复读取(Non-RepeatableRead)。不可重复读取是指**同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。**如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的。所以,Read Uncommitted也无法避免不可重复读取的问题。

    幻读(Phantom Read)。幻读是指同样一个查询在整个事务过程中多次执行后,查询所得的结果集是不一样的幻读针对的是多笔记录。在Read Uncommited隔离级别下,不管事务2的插入操作是否提交,事务1在插入操作之前和之后执行相同的查询,取得的结果集是不同的,所以,Read Uncommitted同样无法避免幻读的问题。

  • Read Commited。Read Committed比Read Uncommitted隔离级别拥有更高级别的限定。在该隔离级别下,一个事务的更新操作结果只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新后的结果。所以,Read Committed可以避免Read Uncommitted隔离级别下存在的脏读问题,但是无法避免不可重复读取和幻读的问题

  • Repeatable Read。Repeatable Read隔离级别可以保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数据的更新提交与否。Repeatable Read隔离级别避免了脏读和不可重复读取的问题,但是无法避免幻读

  • Serializable。它是最为严格的隔离级别。所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别。但同时也是性能最差的隔离级别,因为在该隔离级别下,所有的事务都需要依次顺序执行,所以,并发度下降,吞吐量上不去,性能自然就下来了。因为该隔离级别极大影响系统性能,所以,很少场景会使用它。通常情况下,我们会使用其他隔离级别加上相应的并发锁的机制来控制对数据的访问,这样既保证了系统性能不会损失太大,也能够在一定程度上保证数据的一致性。

对于数据库来说,通常都有一个默认的隔离级别,大多数情况下都是Read Committed(mysql用的是repeatable而不是read committed)。而且,并非所有的数据库都支持这4种隔离级别,比如Oracle只支持Read Committed和Serializable。如果指定的隔离级别当前数据库不支持,那么数据库会采用默认的隔离级别代替我们指定的隔离级别。

EJB、Spring、JDBC等数据访问方式,都允许我们为事务指定以上提到的4种隔离级别,但最终事务是否以指定的隔离级别执行,则由底层的数据资源来决定。

不同的隔离级别设置会对系统的并发性以及数据一致性造成不同的影响。总地来说,隔离级别与系统并发性成反比,与数据一致性成正比。也就是说,事务隔离度越高,系统并发性越差,进而造成系统性能就越差。不过,隔离度的增高,却可以更好地保证数据的一致性。隔离程度与并发性和一致性的关系如图17-1所示。

image-20220410164313750

在具体的实践过程中,我们需要根据系统的具体情况来调整隔离度,保证系统性能与数据一致性之间有一个良好的平衡。总地来说,保证数据的一致性的考虑,应该优于对系统性能的考虑。

4.持久性

事务的持久性是指,一旦整个事务操作成功提交,对数据所做的变更将被记载并不可逆转,多少有点儿“生米煮成熟饭”的意思。即使发生某些系统灾难或者什么天灾人祸之类的事情,之前事务所做的变更也可以找回并恢复。通常情况下,数据库等数据资源管理系统会通过冗余存储或者多数据网络备份等方式,来保证事务的持久性。

至此,对事务自身的认识就算告一段落了。接着,让我们进一步认识一下与事务有关的“家族成员”吧!

初识事务家族成员

在一个典型的事务处理场景中,有以下几个参与者。

  • Resource Manager。简称RM,它负责存储并管理系统数据资源的状态,比如数据库服务器、JMS消息服务器等都是相应的Resource Manager。

  • Transaction Processing Monitor。简称TPM或者TPMonitor,它的职责是在分布式事务场景中协调包含多个RM的事务处理。TP Monitor通常对应特定的软件中间件(Middleware)。随着软件开发技术的进步,TP Monitor的实现也由原来基于过程式的设计与实现,转向面向对象的更趋模块化的设计和实现。通常,J2EE规范”中的应用服务器(Application Server)担当的就是TPMonitor的角色。

  • TransactionManager。简称为TM,它可以认为是TP Monitor中的核心模块,直接负责多RM之间事务处理的协调工作,并且提供事务界定(Transaction Demarcation)、事务上下文传播(Transaction Context Propagation)等功能接口。

  • Application。以独立形式存在的或者运行于容器中的应用程序,可以认为是事务边界的触发点。

实际上,并非每个事务的场景中都会出现以上提到的所有參与者。如果根据整个事务中涉及的RM的多寡来区分事务类型的话,可以将事务分为两类,即全局事务(Global Transaction)和局部事务(Local Transaction)。在这两类事务中,具体的事务参与者是不同的。

更多不再介绍 有兴趣可以看原文

本章小结

在开始对Spring的事务管理层的深层进行探索之前,本章先对事务的一些基本概念和背景进行了一个总体上的回顾,这些只是更高抽象层次上的内容。下面我们将更进一步,详细看一下Java平台上对事务处理都提供了哪些支持。

猜你喜欢

转载自blog.csdn.net/qq_34626094/article/details/124916412