常见分布式事务解决方案
前提
-
mysql的锁机制,行锁,表锁,间隙锁。行锁仅对有索引的查询才生效,是作用在索引上的,锁住的是数据记录。表锁是针对没有索引的字段查询时,会加表锁,或者dba在操作数据库表时会加表锁,表锁锁住的也是数据记录。间隙锁是mysql为了解决RR(可重复读)隔离级别下,当前读的幻读问题,间隙锁不锁数据记录,它锁住的是数据之间的间隙(B+树叶子节点页内数据之间的间隙)
-
mysql的当前读和快照读,所有当前读都会加行锁,常见当前读有selece for update、update、delete,快照度即readview,通过mvcc实现,通过记录数据库每行记录的多个版本来实现,而多个版本的记录是记录在undo log里。
-
mysql事务隔离级别和mvcc的关系。mvcc仅仅和mysql事务隔离级别RR(可重复读)和RC(读已提交)有关,因为读未提交和串行化隔离级别只有一个版本,就是当前版本。
-
单数据源事务,也叫作单机事务或者本地事务,例如mysql单个数据库实现自身的事务特性,通过innodb引擎的undo log 和 redo log 和 aries算法来实现。
-
在分布式场景下,一个系统由多个子系统组成,每个子系统有独立的数据源,多个子系统通过互相调用来组合出复杂的业务。
-
在时下流行的微服务系统架构中,每个子系统就是一个微服务,每个微服务都有自己的独立的数据库。
-
例如电商系统里,购物微服务通过调用库存微服务和订单微服务完成购物过程。但一个数据库的本地事务机制仅仅对落到自身上的查询操作(广义,包括增删改查)起作用,数据库自身提供的本地事务机制无法确保业务对多数据源全局操作的可靠性。由此针对多数据源操作的分布式事务机制就出现了
分布式事务概念
- 分布式事务也叫作全局事务
- 事务参与者, 也叫资源管理器(resource manager)RM,例如每个数据库就是一个事务参与者
- 事务协调者,也叫事务管理器(transaction manager)TM,访问多个数据源的服务程序。TM是一个全局事务管理器,协调多方本地事务的进度,
- 在分布式事务模型中,一个TM管理多个RM,即一个服务程序访问多个数据源
XA协议
- XA协议是x/open提出的分布式事务处理标准。mysql、oracle、db2这些主流数据库都实现了XA标准。
二将军问题
- 计算机网络中普遍存在一个问题,例如,发送者给接受者发送一个http请求,或者mysql客户端给服务端发送一条插入语句,然后超时了没有得到回应,那么客户端蒙圈了,服务器到底是写入成功了还是失败了,可能由于网络故障根本没有到达服务器,可能服务器收到并写入成功了,返回客户端ack前宕机了,或者网络原因导致ack未送到客户端。此时客户端为确保服务端成功写入数据,只能重发,直到收到服务端的响应。
2PC(两阶段提交)
2pc是一种实现分布式事务的简单模型,分为两个阶段:
-
准备阶段:事务协调者向各个事务参与者发起请求,请求各个事务参与者各自开始执行本地事务,执行到待提交阶段(就是除了提交,其他都做了),然后各个事务参与者会回复事务协调者自身事务的执行情况,回复true(表示已准备好,可以提交全局事务),或者回复false(表示某个事务参与者未拿到全局事务所需的本地资源,因为它被其他事务锁住了)或者超时。
-
提交阶段:如果各个事务参与者都返回true,则协调者向所有参与者发起事务提交操作,然后所有参与者执行本地事务的提交操作,并向协调者发送ack,如果任何一个事务参与者回复no或超时,则协调者发送回滚操作,然后所有参与者执行本地事务的回滚操作,并向协调者发送ack。
-
以上可以看出,要实现2PC,所有参与者需要实现3个接口,prepare(),commit(),rollback(),可以将这三个接口简单的理解成XA协议。
-
2PC存在的问题
1、性能差,同步阻塞问题,在准备阶段,协调者需要等待所有参与者返回,才能进入第二阶段。在这期间,各个参与者相关资源被排它锁锁住,会影响各个参与者的本地事务并发度。
2、如果协调者宕机,所有参与者都收不到提交或者回滚命令,各个参与者本地事务资源还处于锁定状态,导致不知所措。
3、如果协调者由于二将军问题未收到某个参与者的ack,协调者将不知所措。
3PC(3阶段提交)
基于2PC,三个阶段分别是:
- 询问阶段:先发请求确保各个参与者网络通信正常,状态正常。
- 准备阶段:同2pc
- 提交阶段:同2pc
+3pc利用超时机制,解决了2pc的同步阻塞问题,避免资源被永久锁定。但同样无法应对宕机问题。
2PC和3PC局限性
- 2PC要求参与者实现XA协议,使用实现了XA协议的数据库可以完成2pc的过程,但在多个微服务通过api相互调用的时候,就不遵循XA协议了,这时候2PC就不适用了,因此2PC只适用于一个服务调用多个库的分布式事务场景,2pc在分布式应用场景中很少使用。
TCC
2PC的使用局限性,我们更多的是要解决多个微服务之间的分布式事务问题。
TCC就是一种解决多个微服务之间分布式事务问题的方案。
Tcc是try、configm、cancel三个单词,其本质是一个应用层面上的2pc,同样分为两个阶段:
-
准备阶段: 协调者调用所有微服务提供的try接口,将整个全局事务涉及到的资源锁定,若锁定成功,try接口向协调者返回yes。
-
提交阶段: 协调者根据所有参与者的ack,确定接下来发送提交(调用confirm接口)还是回滚(调用cancel接口)命令。
-
无论2PC、3PC还是TCC,在出现协调者宕机等事件时,只能通过不断重试来解决。由于try接口锁住了全局事务涉及的所有资源,所以无论提交还是回滚操作失败都可以通过不断重试直到所有服务都返回ack。这里还存在二将军问题,由于不断重试,导致可能重复的调用confirm和cancel接口,所以需要参与者实现接口幂等性。
事务状态表方案
消息事务
消息事务是基于消息中间件的分布式事务最终一致性解决方案。
- 接收方的幂等性
消费者接口幂等性实现,可以建立一个去重表,每个消息都有一个唯一的消息id,每次处理消息先判断,没有处理过再消费。
seata in AT mod 的实现
目前,业界较为成熟的分布式事务框架 seata的AT模式全局事务的实现