分布式数据库原理、架构与实践
上QQ阅读APP看书,第一时间看更新

2.4 分布式事务一致性

事务的一致性在分布式系统下会遇到新的数据异常,引发新的挑战。但解决数据异常的思路,即并发访问控制算法,没有本质上的改变:基本上还是在冲突关系中确认是否存在环,如存在则表示不可串行化(更加深入的讨论参见第4章和第5章)。

事务的一致性和在分布式系统下遇到的分布式一致性,二者需要很好地结合。

本节先简略回顾单机事务的一致性问题,然后从如上两个层面来讨论分布式事务的一致性。

2.4.1 单机事务的一致性

数据库需要保证事务的ACID特性,其中C代表的一致性是数据库ACID特性要保障的数据间关系的目标;A和D是保障数据一致性的两种机制;I是在确保数据一致性(关于数据一致性更为详细的讨论见参考文献[21])正确的基础上,通过允许特定的数据异常发生来换取性能提升的一种机制。这些都属于数据库的事务一致性

维基百科对ACID的定义如下(注意我们使用粗体+斜体的形式标出了关键词)。

  • Atomicity:Atomicity requires that each transaction “all or nothing”。if one part of the transaction fails, then the entire transaction fails, and the database state is left unchanged. An atomic system must guarantee atomicity in each and every situation, including power failures, errors, and crashes. To the outside world, a committed transaction appears (by its effects on the database) to be indivisible (“atomic”), and an aborted transaction does not happen.(原子性:原子性要求每个事务全部成功式失败。如果事务中一部分失败,那么整个事务失败,数据库状态保持不变。原子系统必须保证每种情况下的原子性,包括电源故障、错误和崩溃。在外界看来,提交的事务(通过它对数据库的影响)是不可分割的(原子的),中止事务不会发生。)
  • Consistency:The consistency property ensures that any transaction will bring the database from one valid state to another. Any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This does not guarantee correctness of the transaction in all ways the application programmer might have wanted (that is the responsibility of application-level code) but merely that any programming errors cannot result in the violation of any defined rules.(一致性:一致性用于确保任何事务都能将数据库从一个有效状态进入另一个有效状态。根据所有定义的规则,写入数据库的任何数据都必须是有效的,包括约束、级联、触发器及其任意组合。但这并不能保证事务以应用程序可能希望的所有方式被正确性处理(这是应用程序的责任),而是仅能确保任何编程错误都不能导致违反已定义的规则。)
  • Isolation:The isolation property ensures that the concurrent execution of transactions results in a system state that would be obtained if transactions were executed serially, i.e., one after the other. Providing isolation is the main goal of concurrency control. Depending on the concurrency control method (i.e., if it uses strict - as opposed to relaxed - serializability), the effects of an incomplete transaction might not even be visible to another transaction.(隔离性:隔离性用于确保事务的并发执行会产生一个系统状态,如果事务是串行执行的,即一个接一个地执行,则事务会获得这个系统状态。提供隔离性是并发控制的主要目标。根据并发控制方法,即使用严格的而不是弱的序列化,不完整事务的影响甚至可能导致对另一个事务都是不可见的。)
  • Durability:The durability property ensures that once a transaction has been committed, it will remain so, even in the event of power loss, crashes, or errors. In a relational database, for instance, once a group of SQL statements execute, the results need to be stored permanently (even if the database crashes immediately thereafter). To defend against power loss, transactions (or their effects) must be recorded in a non-volatile memory.(持久性:持久性用于确保一旦事务被提交,即使在系统断电、崩溃或错误的情况下,它也会保持这种状态。例如,在关系数据库中,一旦执行了一组SQL语句,结果就需要永久存储,即使之后数据库立即崩溃。为了防止断电,事务或其影响必须记录在非易失性存储器中。)

对于原子性,上面的定义强调了两种结果——all或nothing,即事务要么成功要么失败。这两种结果因两种动作而产生,即Commit(提交)和Abort(中止);两种结果对应的是事务的两种状态,即COMMITTED和ABORTED。另外,定义中还强调了作为事务的一部分(一个逻辑工作单元中的一个操作),如果失败则整个事务应该是nothing的,换句话说,事务应“一损俱损”但不是“一荣俱荣”。

一致性是一个令人困惑的话题。什么样的情况才算是一致?一个人,言行一致?一组数据,数据与数据之间保持一致?还是如上面的定义所强调的——from one valid state to another(从一个有效状态进入另一个有效状态)?事务的操作使得“特定数据”的状态发生变迁,但前后的结果对应的状态一直是valid(有效)的。那么,什么才是有效的?把salary列的值由1改为2就是有效的吗?答案是:数据在事务的操作下,一直符合all defined rules(所有定义的规则),而这样的规则通常是约束、级联、触发器及其任意组合,即这些规则是数据的“逻辑语义”。比如,写偏序异常,违反的就是特定数据间的约束。约束,属于用户语义限定的数据的一致性。对于一个数据库来说,系统级一致性有另外一层含义,即要想使数据在数据库系统中保持一致,则数据库系统必须符合可串行serializability)和可恢复recoverability)这两个特性。可串行性很重要,其在隔离性下定义,下面会详细描述。可恢复性是指已经提交的事务未曾读过被回滚的事务写过的数据(注意不是指数据库宕机重启后所做的恢复操作,而是不会发生“脏读”)。可恢复性也很重要,当事务回滚,被回滚的事务就不应当对数据的一致性造成影响,数据库的一致性状态是可恢复的。所以,可串行性保障了数据不被并发操作改坏,可恢复性则保障了事务被回滚后数据可回到之前的有效状态[1]

对于隔离性,上面的定义强调了concurrent execution(一次执行),这是指存在多个事务(多个会话中在但同一时间段内运行的不同事务)同时运行时,若它们运行的顺序就好像是serially(串行)的,那么就意味着出现了“并发”看起来像是“串行”的情况,但这并不是说“并发”的这些个动作确实是“串行”的,而是说“并发”的这些个动作对数据操作之后,数据的最终状态应该是有效的,而有效的数据看起来像是“串行”动作造成的。另外,隔离性定义的是可串行化,不要把隔离性与“隔离级别”及“隔离级别”中的“SERIALIZABLE”相混淆。“隔离级别”是为了提高并发度,从而弱化数据在并发读写下的“一致性”(如写偏序),且允许出现参考文献[21]的1.1节所述的数据一致性层级,在这个层级中,只有最高层的SERIALIZABLE才能做到隔离性。

对于持久性,上面的定义强调的是对于已提交(COMMITTED状态)的数据,要能够永久保存,这除了涉及物理存储外,还需要防止处于已提交状态的数据因数据库引擎没有来得及把数据保存到物理存储上(如掉电)而丢失了。所以,从持久性的定义看,持久性对在原子性中提到的中止(ABORTED状态)不关注。

上面我们讨论了ACID特性,这几个特性属于事务的特性。事务的一致性,是数据处理的目标,事务的隔离性是事务处理为实现一致性而对并发事务提出的技术要求(要求并发事务之间互不影响,即是隔离开的)

参考文献[186]认为,事务的一致性就是在以事务为数据操作单位的基础上,使得事务发生前、发生后的数据状态是满足一致性约束的。相关论述如下:一致性约束是显式定义的,如果状态实体的内容满足所有的一致性约束,我们就说状态是一致的。每个事务单独执行时,都会从一个一致性状态转换为另一个新的一致性状态,也就是说,事务会保持一致性。

2.4.2 分布式事务的一致性

分布式数据库系统的事务处理机制,需要遵从单机的事务处理机制,解决单机事务处理遇到的多种数据异常,同时解决分布式系统下特有的数据异常(见参考文献[21]的1.1节,其中描述了单机事务需要解决的数据异常)。1.1.2节简略概述了这些数据异常,并给出分布式数据库需要解决的分布式系统下特有的数据异常。这些数据异常对数据库系统提出挑战,只有解决了这些数据异常,才算实现了分布式事务的一致性。

分布式事务的一致性可以用单机系统下的一致性来表达:分布式事务的一致性是指确保任何事务都能将数据库从一个有效状态转换到另一个有效状态。

事务的一致性着眼于数据库内部,从数据库引擎的角度看,数据状态的变迁满足完整性(逻辑语义上的完整性,非只包括数据库中可通过形式化定义的如CHECK约束般的完整性)约束。

对于1.1.2节描述的Serial-Concurrent-Phenomenon异常,其写操作是一个分布式写操作,写操作没有满足原子性(全局提交完成才算完成,而不是局部节点提交完成即局部提交的数据可见),因此带来了此异常。

对于1.1.2节描述的Cross-Phenomenon异常,局部事务依赖局部节点(单机节点)的事务处理机制来判别本地发生的局部事务与本地发生的全局事务中的一部分之间的关系。这是典型的一叶障目,不能判断所有事务在逻辑全局上是否构成了环,因此可能违背了全部事务的可串行化原则。

对于1.1.2节描述的写偏序、读偏序、只读事务数据异常,在单机系统和分布式系统下均存在,它们的共同之处在于,违反了数据上的应用语义逻辑,从广义的角度看,这种逻辑也是一种完整性约束。

2.4.3 分布式一致性与分布式事务一致性的关系

数据库系统一旦和分布式系统结合,那么在“一致性”这个概念上就易产生混淆。分布式系统会带来“多节点之间对数据读、写的时序在逻辑上是否一致”的问题。这个问题会影响分布式数据库的事务一致性(参见1.1.2节、2.4.1节),也会影响分布式系统的分布式一致性(参见1.1.3节、2.2节、2.3节)。

事务的一致性着眼于在数据系统内部,从数据库引擎的角度保护并发操作不对数据的状态变迁过程造成影响(保持隔离性,确保一致性)。分布式一致性着眼于分布式系统外部,从外部用户的视角看,其可确保读到的数据,符合用户对并发操作发生的顺序的认知。

而分布式事务型数据库,结合了事务的一致性和分布式一致性,内部操作以事务为单位,并同时使得外部观察符合外部一致性,因此分布式事务是二者有机的结合。结合之后的结果,以及最强的分布式事务隔离级别(严格可串行化和顺序可串行化,本书定义的隔离级别),数据库内部不会产生1.1.2节介绍的各种数据异常,但同时又能满足线性一致性。如图2-9所示,结合事务一致性后,PC读取的x的值,依赖于PA的两个写操作是同一个事务还是不同的事务。

如果PA的两个写操作分别属于两个事务,那么即使PA、PB和PC是并发操作,结果也会满足图2-9所示情况。如果PA的两个写操作是同一个事务,那么表明PA和PC是并发操作,结果满足图2-9所示情况。如果采用封锁并发访问控制算法,则PC的读操作只能被阻塞,读取不到值,等PA的事务完成后读到的x值为2;如果采用MVCC并发访问控制算法,则PC的读操作只能读到旧版本的值,即读到的x旧值0。

067-1

图2-9 分布式事务一致性图

上述结果表明,操作以事务为单位,会对分布式系统的一致性的结果产生影响。

再如,式2-11所示是因果一致性的一个例子,如果按照图2-10所示,情况不同,读到的值也会不同。

如图2-10a所示,P1和P2进程上分别发生了两个事务,P2上的读和写操作为同一个事务,此时,P3读到x的值为b之后,则可不能再读到x的值为a,如果允许P3读到xa值则违反了因果一致性;P4如果先读到x的值a,则P4的读操作一定是发生在P2事务开始之前。

如图2-10b所示,P1和P2进程上分别发生了两个事务,P3和P4不能精确判别P1和P2进程上发生的两个事务之间的先后关系,因此P3和P4读取数据不可能不同。如果x数据项只有一个副本,则在这个副本上,修改操作的次序总是能确定的,此时不会产生P3和P4以不同顺序读取到x值的情况,但如果x数据项有多个副本,且副本之间不同步,则可能会产生P3和P4以不同顺序读到x值的情况。

068-1

图2-10 因果一致性的事务图


[1]实现数据库时需要保证:对于并发的事务T1T2,如果T2读取了由T1所写的数据项,则T1应先于T2提交。如果不这样,当T2先提交,但T1被回滚后,则事务T2不可能再被恢复。