热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mysql分布式事务接口_分布式事务总结

分布式事务的产生的原因事务的ACID特性原子性(A)所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,

分布式事务的产生的原因

事务的ACID特性

原子性(A)

所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一样。

一致性(C)

事务的执行必须保证系统的一致性,不会出现中间结果。我个人理解是C和AID特性对事务的描述不是一个层面的,AID是基础特性。只要保证了AID,则C一定会得到满足。

隔离性(I)

所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。

持久性(D)

所谓的持久性,就是说一单事务完成了,那么事务对数据所做的变更就完全保存在了数据库中,即使发生停电,系统宕机也是如此。

我们都知道典型的关系型数据库都是支持单机事务的。单机数据库事务是针对单个数据库的。在分布式场景下,就产生了分布式事务。分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败(事务的原子性)。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

分布式事务产生的场景总结

数据库水平拆分

随着业务数据规模的快速发展,数据量越来越大,单库单表逐渐成为瓶颈。所以我们对数据库进行了水平拆分,将原单库单表拆分成数据库分片。这时候,如果一个操作既访问01库,又访问02库,而且要保证数据的一致性,那么就要用到分布式事务。

业务服务化拆分

随着业务的快速发展,系统的访问量和业务复杂程度都在快速增长,单系统架构逐渐成为业务发展瓶颈,解决业务系统的高耦合、可伸缩问题的需求越来越强烈。目前SOA、微服务架构已经席卷整个互联网行业。将单业务系统拆分成多个业务系统,降低了各系统之间的耦合度,使不同的业务系统专注于自身业务,更有利于业务的发展和系统容量的伸缩。业务系统按照服务拆分之后,一个完整的业务往往需要调用多个服务,多个服务间的数据一致性就是典型的分布式事务场景。

总结

数据表水平拆分后,存在两种跨库操作场景。1)操作同一张逻辑表A的sql实际需要分发到多张物理表(比如A_1,A_2)进行执行。2)同一个业务操作的多张表被分拆到多个库上。针对情况1,应该从业务层面杜绝(需要思考分片健选择是否合理等),如果杜绝不了,也需要在数据库序列化层面拆分为多个操作,也就是拆分为多条sql,然后再在业务层面去保证分布式一致性,而不是让底层数据库支持这种分表的一致性。

业务服务化拆分对应的就是库的拆分,和上面数据表水平拆分的情况2一样。都是库表的重新组装,这种是目前互联网行业普遍存在的分布式事务场景,下面就来说说常见的解决方案。

常见的分布式事务解决方案

分布式解决方案分强一致性解决方案和最终一致性解决方案。强一致性解决方案在一次请求中保证要么成功要么失败回滚到原始状态,是现实上可以分基于二阶段提交和基于回滚接口补偿实现;最终一致性解决方案非实时保证数据的一致性,只保证多个系统最终的状态是一致的,可以分带后向恢复和前向恢复(靠不断重试保证一定成功)的解决方案。

下面以支付场景下单扣款和扣积分为例讲解一下各种解决方案如何实现。

54ab3776ccfe?from=from_parent_mindnote

支付场景

如何保证扣款和加积分保持一致?

强一致性解决方案

强一致性解决方案--XA

XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA实现分布式事务的原理如下:

54ab3776ccfe?from=from_parent_mindnote

XA事务步骤原理

54ab3776ccfe?from=from_parent_mindnote

两阶段提交执行步骤

准备操作(prepare)与ACID

• A: 准备后,仍可提交与回滚

• I: 准备后,事务结果仍然只在事务内可见

• D: 准备后,事务结果已经持久

局限

• 协议成本

• 准备阶段的持久成本

• 全局事务状态的持久成本

• 潜在故障点多带来的脆弱性

• 准备后,提交前的故障引发 一系列隔离与恢复难题

对比一下数据库层面实现差异

单机事务sql

mysql>START transaction 'xatest';

mysql>INSERT INTO mytable (i) VALUES(10);

mysql> ROLLBACK 'xatest';

mysql> COMMIT 'xatest';

XA事务sql

mysql> XA START 'xatest';

mysql> INSERT INTO mytable (i) VALUES(10);

mysql> XA END 'xatest';

mysql> XA PREPARE 'xatest';

mysql> XA ROLLBACK 'xatest';

mysql> XA COMMIT 'xatest';

总的来说,XA协议比较简单,而且一旦商业数据库实现了XA协议,使用分布式事务的成本也比较低。但是,XA也有致命的缺点,那就是性能不理想,之间简单测试了两方事务(就是上面这种两个DB操作的一致性)的性能是单库性能的1/5左右,多方事务的性能会随着参与方的数量成指数下降。XA目前在商业数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换回导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。

强一致性解决方案--基于接口补偿

54ab3776ccfe?from=from_parent_mindnote

基于接口补偿

步骤

1)把扣款和加积分的服务调用放在一个本地方法中。

2)当用户请求登录接口时,先执行加积分操作,加分成功后再执行扣款操作

3)如果扣款成功,那当然最好了,积分也加成功了。如果扣款失败,则调用加积分对应的回滚接口(执行减积分的操作)。

局限

侵入性高,不适用于复杂场景

部分操作不能提供回滚接口

强一致性解决方案 -- TCC编程模式

所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。

54ab3776ccfe?from=from_parent_mindnote

tcc

Try: 尝试执行业务

• 完成所有业务检查(一致性)

• 预留必须业务资源(准隔离性)

Confirm:确认执行业务

• 真正执行业务

• 不作任何业务检查

• 只使用Try阶段预留的业务资源

• Confirm操作满足幂等性

Cancel: 取消执行业务

• 释放Try阶段预留的业务资源

• Cancel操作满足幂等性

与2PC协议比较

• 位于业务服务层而非资源层

• 没有单独的准备(Prepare)阶段,Try操作兼备资源操作与准备能力

• Try操作可以灵活选择业务资源的锁定粒度

• 较高开发成本

TCC具体步骤

54ab3776ccfe?from=from_parent_mindnote

TCC步骤

TCC代码实现

54ab3776ccfe?from=from_parent_mindnote

伪代码实现

实现

• 一个完整的业务活动由一个主业务服务与若干从业务服务组成

• 主业务服务负责发起并完成整个业务活动

• 从业务服务提供TCC型业务操作

• 业务活动管理器控制业务活动的一致性,它登记业务活动中的操作,并在业务活动提交时确认所有的TCC型操作的confirm操作,在业务活动取消时调用所有TCC型操作的 cancel操作

成本

• 实现TCC操作的成本

• 业务活动结束时confirm或cancel操作的执行成本

• 业务活动日志成本

适用范围

• 强隔离性、严格一致性要求的业务活动

• 适用于执行时间较短的业务

最终一致性解决方案

最终一致性描述的是分布式系统中,当系统在数据一致的状态执行更新之后,也应该保持一致的状态。具体实现中可以表现为过程中异步软一致性,但结果要强一致性。消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路:是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。本质上是:将分布式事务转换成两个本地事务,然后依靠下游业务的重试机制达到最终一致性。

Saga是一个长活事务可被分解成可以交错运行的子事务集合。其中每个子事务都是一个保持数据库一致性的真实事务。

Saga介绍

每个Saga由一系列sub-transaction Ti 组成

每个Ti 都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果

可以看到,和TCC相比,Saga没有“预留”动作,它的Ti就是直接提交到库。

Saga的执行顺序有两种:

(1) T1, T2, T3, ..., Tn

(2) T1, T2, ..., Tj, Cj,..., C2, C1,其中0

Saga定义了两种恢复策略:

1)backward recovery,向后恢复,补偿所有已完成的事务,如果任一子事务失败。即上面提到的第二种执行顺序,其中j是发生错误的sub-transaction,这种做法的效果是撤销掉之前所有成功的sub-transation,使得整个Saga的执行结果撤销。

2)forward recovery,向前恢复,重试失败的事务,假设每个子事务最终都会成功。适用于必须要成功的场景,执行顺序是类似于这样的:T1, T2, ..., Tj(失败), Tj(重试),..., Tn,其中j是发生错误的sub-transaction。该情况下不需要Ci。

显然,向前恢复没有必要提供补偿事务,如果你的业务中,子事务(最终)总会成功,或补偿事务难以定义或不可能,向前恢复更符合你的需求。

Saga看起来很有希望满足我们的需求。所有长活事务都可以这样做吗?这里有一些限制:

Saga只允许两个层次的嵌套,顶级的Saga和简单子事务

在外层,全原子性不能得到满足。也就是说,sagas可能会看到其他sagas的部分结果

每个子事务应该是独立的原子行为

补偿也有需考虑的事项:补偿事务从语义角度撤消了事务Ti的行为,但未必能将数据库返回到执行Ti时的状态。(例如,如果事务触发导弹发射, 则可能无法撤消此操作)

协调式Saga VS编排式Saga

当系统命令启动saga时,协调逻辑必须选择并告知第一个saga参与者执行本地事务。一旦该事务完成,saga的排序协调选择并调用下一个saga参与者。这个过程一直持续到saga执行了所有步骤。如果任何本地事务失败,则saga必须以相反的顺序执行补偿事务。构建一个saga的协调逻辑有几种不同的方法:

1)协同式(Choreography):把Saga的决策和执行顺序逻辑分布在Saga的每一个参与方中,它们通过交换事件的方式来进行沟通。

2)编排式(Orchestration):把Saga的决策和执行顺序逻辑集中在一个Saga编排器类中。Saga编排器发出命令式消息给各个参与方,指示这些参与方服务完成具体的本地事务操作。

更多关于两种saga的描述可以参考附录中saga的引用文章。

saga落地--个人看法

不管是协调式saga还是编排式saga,都只允许两个层次的嵌套。saga倡导通过事件命令来实现不同参与方的通信。但是现实场景中,消息是属于异步通信,存在延迟的可能性,与异步消息对应的是实时RPC,很多对延迟敏感的业务场景,与参与方之间的通信都是采用RPC。同时,我们需要区分参与方的重要程度,有些参与方是跟场景密切相关,时效性要求很高,可以说是生死与共;而有些参与方跟场景的重要性不高,对时效没有要求。比如电商下单场景下,扣库存、支付、扣积分等行为是生死与共的,下单用户提醒IM消息、物流等相对就没那么重要,失败后异步重试都能接受。

我们可以借鉴编排式saga的思想,在分布式事务场景里面通过延迟回滚消息来实现参与方共进退的统一协调(而不是通过一个显示的流程定义来实现)。通过将实时性要求高&&重要的请求的改为RPC交互,其他用异步消息模式。下面以电商下单场景为例,看如何变种saga满足绝大部分场景下的分布式事务需求:

54ab3776ccfe?from=from_parent_mindnote

变种saga流程

1)交易系统创建订单(往DB插入一条记录),同时发送订单创建消息。通过RocketMq事务性消息保证一致性。

2)接着执行完成订单所需的同步核心RPC服务(非核心的系统通过监听MQ消息自行处理,处理结果不会影响交易状态)。执行成功更改订单状态,同时发送MQ消息。

3)交易系统接受自己发送的订单创建消息,通过定时调度系统创建延时回滚任务(或者使用RocketMq的重试功能,设置第二次发送时间为定时任务的延迟创建时间。在非消息堵塞的情况下,消息第一次到达延迟为1ms左右,这时可能RPC还未执行完,订单状态还未设置为完成,第二次消费时间可以指定)。延迟任务先通过查询订单状态判断订单是否完成,完成则不创建回滚任务,否则创建。 PS:多个RPC可以创建一个回滚任务,通过一个消费组接受一次消息就可以;也可以通过创建多个消费组,一个消息消费多次,每次消费创建一个RPC的回滚任务。 回滚任务失败,通过MQ的重发来重试。

以上是交易系统和其他系统之间保持最终一致性的解决方案。

本地事件表 VS 事务消息

本地时间表和事务消息本质上都是保证MQ发送和本地DB操作原子性(要么一起成功、要么一起失败)。目前MQ中支持事务消息的只有Rocketmq。

Rocketmq事务消息

54ab3776ccfe?from=from_parent_mindnote

流程图

上图是RocketMQ提供的保证MQ消息、DB事务一致性的方案。

MQ消息、DB操作一致性方案:

1)发送消息到MQ服务器,此时消息状态为SEND_OK。此消息为consumer不可见。

2)执行DB操作;DB执行成功Commit DB操作,DB执行失败Rollback DB操作。

3)如果DB执行成功,回复MQ服务器,将状态为COMMIT_MESSAGE;如果DB执行失败,回复MQ服务器,将状态改为ROLLBACK_MESSAGE。注意此过程有可能失败。

4)MQ内部提供一个名为“事务状态服务”的服务,此服务会检查事务消息的状态,如果发现消息未COMMIT,则通过Producer启动时注册的TransactionCheckListener来回调业务系统,业务系统在checkLocalTransactionState方法中检查DB事务状态,如果成功,则回复COMMIT_MESSAGE,否则回复ROLLBACK_MESSAGE。

说明:

上面以DB为例,其实此处可以是任何业务或者数据源。

以上SEND_OK、COMMIT_MESSAGE、ROLLBACK_MESSAGE均是client jar提供的状态,在MQ服务器内部是一个数字。

TransactionCheckListener 是在消息的commit或者rollback消息丢失的情况下才会回调(上图中灰色部分)。这种消息丢失只存在于断网或者rocketmq集群挂了的情况下。

本地事件表如何与DDD相得益彰

通常的业务处理过程都会更新数据库然后发布领域事件,这里一个比较重要的点是:我们需要保证数据库更新和事件发布之间的原子性,也即要么二者都成功,要么都失败。如果我们的mq中间件不支持事务消息,一种较佳的实现方式是通过事件表来保证消息和本地事务的一致性。流程大致如下:

54ab3776ccfe?from=from_parent_mindnote

事件表步骤

在更新业务表的同时,将领域事件一并保存到数据库的事件表中,此时业务表和事件表在同一个本地事务中,即保证了原子性,又保证了效率。

在后台开启一个任务(或者基于监听事件表的binlog),将事件表中的事件发布到消息队列中,发送成功之后删除掉事件。

发布领域事件的整个流程如下:

接受用户请求;

处理用户请求;

写入业务表;

写入事件表,事件表和业务表的更新在同一个本地数据库事务中;

事务完成后,即时触发事件的发送(可以定时扫描事件表,还可以借助诸如MySQL的binlog之类的机制);

后台任务读取事件表;

后台任务发送事件到消息队列;

发送成功后删除事件。

在事件表场景下,一种常见的做法是将领域事件保存到聚合根中,然后在Repository保存聚合根的时候,将事件保存到事件表中。

总结

分布式解决方案

简介

优缺点

XA两阶段提交

资源管理器(数据库等存储管理结点)实现Xa规范

协议成本带来交互成本高、持久成本高。虽能保障强一致性,单并发能力弱

提供回滚接口

业务方依次执行,出现异常调用回滚接口

业务侵入性高。一致性实时性高

TCC模式

原来一个接口需要改成三个接口,TCC框架通过拦截器捕获Try操作异常,决定调用Confirm对应接口还是Cancal对应接口

开发成本高,一致性实时性强

saga

通过消息将长活事务分解成可以交错运行的子事务集合,其中每个子事务都是一个保持数据库一致性的真实事务。

只能满足最终一致性,需要事务消息中间件或者使用事件表;优点是业务方开发成本低

分布式事务,本质上是对多个数据库的事务进行统一控制,按照控制力度可以分为:不控制、部分控制和完全控制。不控制就是不引入分布式事务,部分控制就是各种变种的两阶段提交。部分控制的好处是并发量和性能很好,缺点是数据一致性减弱了,完全控制则是牺牲了性能,保障了一致性,具体用哪种方式,最终还是取决于业务场景。作为技术人员,一定不能忘了技术是为业务服务的,不要为了技术而技术,针对不同业务进行技术选型也是一种很重要的能力。

参考



推荐阅读
  • 小王详解:内部网络中最易理解的NAT原理剖析,挑战你的认知极限
    小王详解:内部网络中最易理解的NAT原理剖析,挑战你的认知极限 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 第二章:Kafka基础入门与核心概念解析
    本章节主要介绍了Kafka的基本概念及其核心特性。Kafka是一种分布式消息发布和订阅系统,以其卓越的性能和高吞吐量而著称。最初,Kafka被设计用于LinkedIn的活动流和运营数据处理,旨在高效地管理和传输大规模的数据流。这些数据主要包括用户活动记录、系统日志和其他实时信息。通过深入解析Kafka的设计原理和应用场景,读者将能够更好地理解其在现代大数据架构中的重要地位。 ... [详细]
  • 近年来,BPM(业务流程管理)系统在国内市场逐渐普及,多家厂商在这一领域崭露头角。本文将对当前主要的BPM厂商进行概述,并分析其各自的优势。目前,市场上较为成熟的BPM产品主要分为两类:一类是综合型厂商,如IBM和SAP,这些企业在整体解决方案方面具有明显优势;另一类则是专注于BPM领域的专业厂商,它们在特定行业或应用场景中表现出色。通过对比分析,本文旨在为企业选择合适的BPM系统提供参考。 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 如何有效防御网站中的SQL注入攻击
    本期文章将深入探讨网站如何有效防御SQL注入攻击。我们将从技术层面详细解析防范措施,并结合实际案例进行阐述,旨在帮助读者全面了解并掌握有效的防护策略。希望本文能为您的网络安全提供有益参考。 ... [详细]
  • (1)前期知识:1. 单机架构:单一服务器计算机——其处理能力和存储容量有限。2. 集群架构(负载均衡器与多节点服务器)——通过增加节点数量来提升系统性能和可靠性,实现高效的任务分配和资源利用。 ... [详细]
  • Python 实战:异步爬虫(协程技术)与分布式爬虫(多进程应用)深入解析
    本文将深入探讨 Python 异步爬虫和分布式爬虫的技术细节,重点介绍协程技术和多进程应用在爬虫开发中的实际应用。通过对比多进程和协程的工作原理,帮助读者理解两者在性能和资源利用上的差异,从而在实际项目中做出更合适的选择。文章还将结合具体案例,展示如何高效地实现异步和分布式爬虫,以提升数据抓取的效率和稳定性。 ... [详细]
  • 解读中台架构:微服务与分布式技术的区别及应用
    中心化与去中心化是长期讨论的话题。中心化架构的优势在于部署和维护相对简单,尤其在服务负载较为稳定的情况下,能够提供高效稳定的性能。然而,随着业务规模的扩大和技术需求的多样化,中心化架构的局限性逐渐显现,如扩展性和故障恢复能力较差。相比之下,微服务和分布式技术通过解耦系统组件,提高了系统的灵活性和可扩展性,更适合处理复杂多变的业务场景。本文将深入探讨中台架构中微服务与分布式技术的区别及其应用场景,帮助读者更好地理解和选择适合自身业务的技术方案。 ... [详细]
  • 如果程序使用Go语言编写并涉及单向或双向TLS认证,可能会遭受CPU拒绝服务攻击(DoS)。本文深入分析了CVE-2018-16875漏洞,探讨其成因、影响及防范措施,为开发者提供全面的安全指导。 ... [详细]
  • 深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用
    深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用 ... [详细]
  • 本文推荐了六款高效的Java Web应用开发工具,并详细介绍了它们的实用功能。其中,分布式敏捷开发系统架构“zheng”项目,基于Spring、Spring MVC和MyBatis技术栈,提供了完整的分布式敏捷开发解决方案,支持快速构建高性能的企业级应用。此外,该工具还集成了多种中间件和服务,进一步提升了开发效率和系统的可维护性。 ... [详细]
  • 在什么情况下MySQL的可重复读隔离级别会导致幻读现象? ... [详细]
author-avatar
i89379844
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有