分布式事务

  事务机制是可以保证一系列写操作要么全部完成,要么全部不会完成,不会发生只完成一系列中一两个写操作,事务机制缺省一般由数据库完成,也可以通过应用程序实现(如Java的JTA等)

  事务由四个属性,简称ACID:

  1. Atomicity原子性:一个事务的所有系列操作步骤被看成是一个操作,也就是一个原子操作,打个比喻:多个SQL语句如同一个SQL执行一样。
  2. Consistency一致性:如果两个以上数据表有关联,那么更新一个表同时另外一个表也要一起更新,否则两个表的数据记录就发生不一致了。
  3. Isolation隔离性:主要用于实现并发控制, 隔离能够确保并发执行的事务能够顺序一个接一个执行,通过隔离,一个未完成事务不会影响另外一个未完成事务,隔离是通过用悲观或乐观锁机制实现的。
  4. Durability耐久性:一个成功的事务将永久性地改变系统的状态,所以在它结束之前,所有导致状态的变化都记录在一个持久的事务日志中。如果我们的系统突然受到系统崩溃或断电,那么所有未完成已提交的事务可能会重演。

  关系数据库提供默认不同级别的ACID,完全真正严格的ACID会导致性能下降,因此,事务代表的可靠性和性能扩展性是一对矛盾,如何进行平衡是架构设计的主要考量,特别是在分布式系统中如何保证ACID事务是一项极具有挑战力的工作。

2PC

  2PC(2 phase commit)是一种分布式事务进行两段事务提交的简称,JavaEE的JTA/XA是2PC一种实现。2PC适合有多个数据源情况下统一按照ACID原则完成操作,比如一个操作涉及三个数据库的三个表a b c,如何保证这三个表的数据同时操作完成,保证在同一逻辑下的一致性,这是2PC关注所在,如果没有2PC,有可能a表修改成功,b表和c表没有修改成功,那么就出现不一致性。

  下图是传统分布式2PC两段事务提交示意图

  两段提交协议是一种分布式资源的原子确认协议,这个协议是通过两段过程完成业务数据的更改:第一段是预准备阶段,事务管理器通知所有分布式资源准备接受提交或退出事务,第二阶段,事务管理器安装每个分布式资源的回应情况决定是真正提交完成事务或者退出事务。在第一阶段,我们通过服务的JTA事务修改的数据库数据并没有真正写入数据库,只有最后阶段完成时才真正写入。

  以转账案例为说明,A账户转到B账户100元,A账户余额应该减去100元,而B帐号增加100元,如果A账户在上海机房服务器,B帐号在北京机房,那么通过2PC保证这种加减一致性,如果没有2PC,A帐号已经减去100元,但是B账户却没有增加100元,整个操作也没有报错回滚,所以,2PC是保证业务逻辑正确性,精确性的。

  注意:2PC的这种分布式数据一致性与CAP定理中分布式数据一致性是有区别的。

2PC的问题

  2PC的问题主要由于并发性能不高,由于一段事务需要经过两个阶段,这两个阶段需要将资源上锁,而分布式环境中网络抖动是不可避免的,因此容易造成事务失败,或者由于事务正在进行中,锁定资源太多,比如锁定数据表记录太多,导致其他事务等待时间较长。区块链等技术由于采取每个区块链接到上一个区块,类似一个LinkedList,虽然保证了分布式数据的可靠性,但是吞吐量和并发性能却不高,两者机制是差不多,2PC性能反而会好一些,因为数据库资源相对是集中的。

  传统2PC分布式事务能够保证数据可靠性,但是无法提高吞吐量和并发性能,如何进行权衡?这就需要引入分布式系统的定理:CAP定理。

  通过降低传统2PC的强一致性,使用弱一致性替代,从而就能在出现网络抖动导致分区的情况下提高可用性,等网络连通正常后,再进行数据同步复制,保证数据的一致性,这种方式也称为柔性事务。其中Saga事务是一种分布式柔性事务的探索。

  还有一种TCC(Try-Confirm-Cancel)其实是JTA 2PC的一种补充,TCC中第一个C是提交的意思,TCC中第二个C如果看成是类似2PC的回滚Rollback,那么其实就是两段提交;如果将第二个C理解为补偿有些牵强,补偿的意思是不断新增补充,而不是撤销回退原来的动作,补偿本身概念有始终向前的意思,既然有补偿概念,就需要有过去动作的历史记录,没有历史记录你怎么补偿,你如果撤销删除了以前的动作就不是补偿,如同你把财务账本上一条记录用涂改液擦除了,那是要犯法的,补偿就是你不能回退擦除以前的记录,只能新增一条记录,来冲抵以前的错误记录,所以,谈到补偿肯定需要类似账本的流水记录的(区块链本身就是一个账本)。

  分布式事务有很多探索,比如使用Paxo/Raft实现分布式环境下的数据高一致性,Google的Spanner和FoundationDB以及微软的Azure Cosmos DB都属于分布式强一致性事务的数据库。

  还有一种分布式事务机制,使用事件溯源方式,再引入Kafka这样的原子消息系统,确保消息送达目标,同时保证在目标实现单写方式(Stream流),这种使用事件同步替代状态同步,从而能实现间接实现多个节点之间的数据最终一致性。这个思路可见本站Jdon框架的转账演示DEMO.

 

教程与文章

从架构师思维看分布式事务

《复杂软件设计之道:领域驱动设计全面解析与实战》(本站原创书籍在事件溯源章节讨论分布式事务)

微服务架构中的分布式事务全面详解

什么是数据库ACID

关系数据库是如何工作的?

Java和Spring中的事务简介 - Baeldung

JDBC基础教程

JDBC事务锁基础教程

JDBC悲观锁与乐观锁基础教程

JDBC隔离级别基础教程

ACID和CAP的详尽比较

业界最大谎言:大部分关系数据库并不真的支持ACID

Java持久锁总结

数据库系统并发控制原理

PostgreSQL、Oracle/MySQL和SQL Server的MVCC实现原理方式

最终一致性其实比MVCC简单

线性化与串行化比较

为什么大部分NoSQL不提供分布式事务?

比特币区块链是一种分布式的事件流日志

分布式事务可能是个伪概念

两个领域事件驱动的开源项目介绍

CAP定理在分布式系统设计中的最新应用

超越分布式事务

微服务分布式事务Saga模式简介

Eventuate:基于操作CRDT的服务框架

替代传统事务的并发建议

LMAX微服务级别的分布式事务实现

著名的分布式事务数据库谷歌Spanner设计有坑!

Saga与工作流引擎比较

分布式共识如何运作?

数据库数据复制技术入门

两段事务提交2PC的缺点和解决之道

DDD实践:在SpringBoot中跨微服务通过发件箱模式实现分布式事务机制 - Hans-Peter Grahsl

分布式事务的替换者:在线事件处理OLEP(事件溯源)

Spring事务管理:非常规指南 - marcobehler

2PC的时代即将结束,但是围绕2PC仍存在许多误解,2PC只是提供原子性而不是事务 · Exactly Once

金融领域微服务架构中如何实现分布式事务?如何记录更多事件,存储在哪里?事件顺序如何保证? - Revolut

如何微服务在实现分布式事务的变通?

DDD实践:在SpringBoot中跨微服务通过发件箱模式实现分布式事务机制 - Hans-Peter Grahsl

Redis如何简化微服务设计模式

在使用Kafka+微服务发送聚合的领域事件时如何在错误重试时保证顺序?

在微服务架构中实施分布式事务锁的几个方案比较

Apache Kafka不是数据库:数据库+Kafka=完整ACID

 

参考话题:

分布式系统

分布式架构

分布式CAP定理

分布式共识一致性教程

NoSQL数据库

中台数据工程教程

软件弹性工程与设计

分布式事务教程

#Saga事务 #事务架构 #JTA事务 #分布式事务

#微服务专题 #数据库 #工作流 #区块链