个人学习分布式事务的总结,引用了很多优秀文章的阐述。如有版权问题,请告知。
事务的特点
事务拥有以下四个特性,习惯上被称为ACID特性。
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态是指数据库中的数据应满足完整性约束。除此之外,一致性还有另外一层语义,就是事务的中间状态不能被观察到(这层语义也有说应该属于原子性)。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行,如同只有这一个操作在被数据库所执行一样。
- 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。在事务结束时,此操作将不可逆转。
本地事务
本地事务一般指数据库单库事务。传统关系数据库对事务支持已经非常完善。主流的开发框架,比如Spring,针对传统数据库的事务提供了便捷的支持,通过配置或者注解等方式来控制事务。
分布式事务
规模庞大的系统,就要跟分布式事务打交道了。主要的一些业务场景如下。
分库分表
数据库做水平切分时,一个SQL操作会涉及多个数据库,这些操作需要在一个事务中完成。MyCat支持分布式事务。
微服务
业务拆分成众多微服务之后,业务的调用链条变长了,可能涉及众多服务、数据库、消息服务,所有的资源操作需要在一个事务中完成。
两阶段提交
XA是一个分布式事务协议。XA分为两部分:事务管理器(Transaction Manager)和本地资源管理器(Resource Manager)。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。2PC和3PC提交都是基于XA协议实现的。
两阶段提交的问题
同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
TCC
补偿性事务大致含义是,"补偿是一个独立的支持ACID特性的本地事务,用于在逻辑上取消服务提供者上一个ACID事务造成的影响,对于一个长事务(long-running transaction),与其实现一个巨大的分布式ACID事务,不如使用基于补偿性的方案,把每一次服务调用当做一个较短的本地ACID事务来处理,执行完就立即提交”。
confirm和cancel就是补偿事务,用于取消try阶段本地事务造成的影响。因为第一阶段try只是预留资源,之后必须要明确的告诉服务提供者,这个资源你到底要不要,对应第二阶段的confirm/cancel。
可靠消息最终一致性
可靠消息最终一致性是指产生消息的业务动作与消息发送的一致。也就是说,如果业务操作成功,那么由这个业务操作所产生的消息一定要成功投递出去(一般是发送到kafka、rocketmq、rabbitmq等消息中间件中),否则就丢消息。
阿里云提供的MQ和MNS两种消息产品都支持事务型消息,将这个特性跟数据库事务结合,可以实现基于可靠消息的最终一致性。
GTS
GTS是阿里云提供的全局事务服务。GTS 支持 DRDS、RDS、Oracle、MySQL、PostgreSQL、OceanBase 和 Petadata 等多种数据源,可以配合 EDAS、Dubbo 和 Spring Cloud 等微服务框架使用, 兼容 MQ 实现事务消息。通过各种组合,可以轻松实现分布式数据库事务、多库事务、消息事务、服务链路级事务等多种业务需求。
针对不同的应用场景,GTS 主要提供标准模式(AT)和自定义模式(MT)两种事务模式。AT模式会依赖数据库,用户需要创建txc_undo_log
表。MT模式更像TCC,用户需要自己实现两阶段提交的逻辑。
AT 模式
AT 模式是 GTS 最主要的事务模式,通过 GTS 基于 DRDS/RDS 的数据源,对 SQL 语句提供分布式事务支持。它帮助应用方以最小的改造代价来实现数据库的事务功能。
AT 模式适合于 DRDS 分库分表、多数据库数据源、跨进程的多数据库数据源等几乎任何 DRDS 应用场景下的分布式事务。
MT 模式
MT模式提供用户可以介入两阶段提交过程的一种模式。在这种模式下,用户可以根据自身业务需求自定义在 GTS 的两阶段中每个阶段的具体行为。MT 模式提供了更多的灵活性,可能性,以达到特殊场景下的自定义优化,及特殊功能的实现。
MT 模式不依赖于数据库,这是它相对于 AT 模式的一个最大的优势。MT 模式几乎满足任何事务场景。
样例
GTS提供了丰富的样例帮助大家理解产品的原理和使用。
基于消息的最终一致性
sample-txc-mq
这个例子使用MQ实现的最终一致性。循环转账十次,前五次会成功,后五次会因为余额不足而失败。
[root@izuf60wa1jflm222pngldrz bin]# ./run.sh
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
JM.Log:INFO Init JM logger with Slf4jLoggerFactory success, sun.misc.Launcher$AppClassLoader@4617c264
JM.Log:INFO Log root path: /root/logs/
JM.Log:INFO Set txc log path: /root/logs/txc
JM.Log:INFO Set diamond-client log path: /root/logs/diamond-client
client mode:3 [0:None (only be client) 1:Default Mode 2:Manual Mode 3:Default Mode & Manual Mode 5:Default Mode & Service Mode 6:Manual Mode & Service Mode 7:Default Mode & Manual Mode &Service Mode]
txcAppName:gts_henshao_test.1162142976628250.SH
txcServerGroup:gts_henshao_test.1162142976628250.SH
Sat Dec 15 15:56:46 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Sat Dec 15 15:56:46 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Producer started!
Sat Dec 15 15:56:46 CST 2018
send msgId:AC139F2C46554617C26448F1D3960000 status:SEND_OK broker:shanghaishare-10 qid:0, topic:mq_henshao_test
A的中间状态为:80元
本次转账结束,A和B的余额为:
A账户现在余额为80元
B账户现在余额为120元
A和B的金额总和为200元
Sat Dec 15 15:56:47 CST 2018
Sat Dec 15 15:56:48 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Sat Dec 15 15:56:48 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
send msgId:AC139F2C46554617C26448F1D6BC0003 status:SEND_OK broker:shanghaishare-10 qid:0, topic:mq_henshao_test
A的中间状态为:60元
本次转账结束,A和B的余额为:
A账户现在余额为60元
B账户现在余额为140元
A和B的金额总和为200元
Sat Dec 15 15:56:48 CST 2018
send msgId:AC139F2C46554617C26448F1D9D10006 status:SEND_OK broker:shanghaishare-10 qid:0, topic:mq_henshao_test
A的中间状态为:40元
本次转账结束,A和B的余额为:
A账户现在余额为40元
B账户现在余额为160元
A和B的金额总和为200元
Sat Dec 15 15:56:49 CST 2018
send msgId:AC139F2C46554617C26448F1DA7E0009 status:SEND_OK broker:shanghaishare-10 qid:0, topic:mq_henshao_test
A的中间状态为:20元
本次转账结束,A和B的余额为:
A账户现在余额为20元
B账户现在余额为180元
A和B的金额总和为200元
Sat Dec 15 15:56:49 CST 2018
send msgId:AC139F2C46554617C26448F1DAFE000C status:SEND_OK broker:shanghaishare-10 qid:0, topic:mq_henshao_test
A的中间状态为:0元
本次转账结束,A和B的余额为:
A账户现在余额为0元
B账户现在余额为200元
A和B的金额总和为200元
Sat Dec 15 15:56:49 CST 2018
send msgId:AC139F2C46554617C26448F1DB80000F status:SEND_OK broker:shanghaishare-11 qid:0, topic:mq_henshao_test
A的中间状态为:-20元
not enough money. 回滚到事务前状态
本次转账结束,A和B的余额为:
A账户现在余额为0元
B账户现在余额为200元
A和B的金额总和为200元
Sat Dec 15 15:56:50 CST 2018
send msgId:AC139F2C46554617C26448F1DCF60012 status:SEND_OK broker:shanghaishare-11 qid:0, topic:mq_henshao_test
A的中间状态为:-20元
not enough money. 回滚到事务前状态
本次转账结束,A和B的余额为:
A账户现在余额为0元
B账户现在余额为200元
A和B的金额总和为200元
Sat Dec 15 15:56:50 CST 2018
send msgId:AC139F2C46554617C26448F1DDC20015 status:SEND_OK broker:shanghaishare-11 qid:0, topic:mq_henshao_test
A的中间状态为:-20元
not enough money. 回滚到事务前状态
本次转账结束,A和B的余额为:
A账户现在余额为0元
B账户现在余额为200元
A和B的金额总和为200元
Sat Dec 15 15:56:50 CST 2018
send msgId:AC139F2C46554617C26448F1DE870018 status:SEND_OK broker:shanghaishare-11 qid:0, topic:mq_henshao_test
A的中间状态为:-20元
not enough money. 回滚到事务前状态
本次转账结束,A和B的余额为:
A账户现在余额为0元
B账户现在余额为200元
A和B的金额总和为200元
Sat Dec 15 15:56:50 CST 2018
send msgId:AC139F2C46554617C26448F1DF75001B status:SEND_OK broker:shanghaishare-11 qid:0, topic:mq_henshao_test
A的中间状态为:-20元
not enough money. 回滚到事务前状态
本次转账结束,A和B的余额为:
A账户现在余额为0元
B账户现在余额为200元
A和B的金额总和为200元
wait for several seconds ------------------
checking result --------------------
A原来来有100元,现在为0元
B原来来有100元,现在为200元
A和B的金额总和为200元
三个分支事务分别是两个数据库,一个MQ消息。
去MQ里面可以看到一共发送了五条消息,失败的五条是看不到的。
预留和补偿事务
MT模式的两个例子,预留事务是在rollin的时候预留资源,commitRollin的时候操作资源及删除分支事务的数据。补偿事务则是在rollin的时候就操作资源,commitRollin的时候只删除分支事务的数据。