当前位置: 首页 > 科技观察

五种分布式事务你了解多少?

时间:2023-03-13 05:48:40 科技观察

本文转载自微信公众号“木小农”,作者木小农。转载本文请联系沐小农公众号。1.前言交易(Transaction):泛指做什么或做什么,由开始交易到结束交易之间进行的所有操作组成。简单地说:要么全部执行,要么全部失败。分布式事务自然是运行在分布式系统中的事务,由多台不同机器上的事务组成。如上,只有分布式系统中的所有事务都执行完才能成功,否则会失败。事务的基本特征ACID:原子性(Atomicity):事务是一个不可分割的工作单元,事务中包含的所有操作要么完成要么不完成。一致性:表示交易执行前后数据是完整的。隔离性:一个事务的执行不能被其他事务干扰。即一个事务内使用的操作和数据与其他并发事务隔离,并发执行的事务之间不能相互干扰。持久性:也称为永久性,一旦事务被提交,它对数据库中数据的更改应该被永久保存。2、分布式事务的目标和实际应用场景分布式事务的目标:解决多个独立事务的一致性问题。例如,我们有一个功能,订单系统,它跨越多个微服务。由于各个微服务不在同一个库中,因此无法使用数据库事务来保证事务。那么这个时候我们就可以使用分布式事务了。例如:商城项目,用户支付了一个订单,在支付系统中,更新了支付表,在另一个订单系统中,订单库中的订单状态会变成已支付,然后在订单表中和支付表,它们是不同的如何保证两个数据库之间的交易?支付操作:支付,修改余额,修改订单状态3.分布式事务解决方案两阶段提交协议(2PC)三阶段提交协议(3PC)补偿交易(TCC)消息中间件实现了seata框架4.两者-phasecommitprotocol(2PC)基于XA协议,采用强一致性,符合ACID2PC:(2-phasecommitprotocol),基于XA/JTA规范。4.1XAXA是X/Open组织提出的分布式事务架构(或协议)。XA架构主要定义了(全局)事务管理器(TransactionManager)和(本地)资源管理器(ResourceManager)之间的接口。XA接口是一个双向的系统接口,它构成了事务管理器(TransactionManager)和一个或多个资源管理器(ResourceManager)之间的沟通桥梁。也就是说,在基于XA的事务中,我们可以对多个资源进行事务管理,比如一个系统访问多个数据库,或者既访问数据库又访问消息中间件等资源。这样我们就可以直接在多个数据库和消息中间件中实现全部提交或者全部取消的事务。XA规范不是java规范,而是通用规范。4.2JTAJTA(JavaTransactionAPI),是J2EE编程接口规范,是XA协议的JAVA实现。主要定义了:一个事务管理器的接口javax.transaction.TransactionManager,定义了相关事务的启动、提交、退出等操作。满足XA规范的资源定义接口javax.transaction.xa.XAResource。如果一个资源要支持JTA事务,就需要让其资源实现XAResource接口,并实现该接口定义的两阶段提交相关接口。4.3流程图4.4提交流程1.请求阶段,(commit-requestphase,orvotingphase,votingphase),步骤(1-5)在请求阶段,协调器会通知交易参与者准备提交或取消交易,然后进入投票程序。在投票过程中,参与者会将自己的决定告知协调器:同意(交易参与者本地作业执行成功)或取消(本地作业执行失败)。2.提交阶段,步骤(6-7)在这个阶段,协调者将根据第一阶段的投票结果做出决定:提交或取消。当且仅当所有参与者都同意提交交易时,协调器才会通知所有参与者提交交易,否则协调器会通知所有参与者取消交易。参与者收到协调者的消息后,会进行相应的操作。4.5缺点单点故障:事务的发起、提交或取消都由bosscoordinator管理。只要coordinatordown了,就爽了。同步阻塞的缺点:从上面的介绍和例子可以看出,当我们的参与系统没有收到老板真正的commit或者cancel事务指令时,只是锁住当前资源,并没有真正做一些事务相关的操作。因此,整个分布式系统环境都被阻塞了。数据不一致的缺点:也就是说,当老大协调员向小弟发送真正的提交交易时,一些网络故障导致一些系统收不到真正的指令,那么一些提交就不会被提交,所以这会导致数据不一致。4.6无法解决的问题当协调者出错,参与者同时出错时,二相无法保证交易执行的完整性。考虑协调器在发送提交消息后宕机,唯一收到消息的参与者也同时宕机。那么即使有新的协调者,这个事务的状态也是不确定的,谁也不知道这个事务有没有提交。知道的人都沉默了。5、三阶段提交协议(3PC)采用强一致性,遵守ACID。第二阶段新增:超时和预提交机制。主要分为三个阶段,canCommit、preCommit和doCommit。5.1流程图5.2流程1.CanCommit阶段:3PC的CanCommit阶段其实和2PC的准备阶段非常相似。协调者向参与者发送提交请求,参与者如果可以提交则返回Yes响应,否则返回No响应。2、PreCommit阶段:Coordinator根据Cohort的响应决定是否继续对该事务进行PreCommit操作。根据响应,有两种可能性。A.如果Coordinator得到所有Cohorts的Yes响应,它将执行事务的预执行:发送预提交请求。Coordinator向Cohort发送PreCommit请求,进入Prepared阶段。交易预提交。Cohort(一群士兵)收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。回应反馈。如果Cohort成功执行了事务操作,它返回一个ACK??响应并开始等待最终命令。B.如果有任何Cohort向Coordinator发送No响应,或者等待超时后,Coordinator没有收到Cohort响应,则中断事务:发送中断请求。协调器向所有队列发送中止请求。中断业务。Cohort收到Coordinator的abort请求后(或超时后,尚未收到Cohort请求),事务中断。3.DoCommit阶段:在这个阶段,真正的事务提交可以分为以下两种情况:执行commitA.发送commit请求。Coordinator收到Cohort发来的ACK响应,就会从预提交状态进入提交状态。并向所有Cohorts发送doCommit请求。B.事务提交。Cohort收到doCommit请求后,进行正式的事务提交。并在完成事务提交后释放所有事务资源。C.回应反馈。事务提交后,向Coordinator发送ACK响应。D.完成交易。Coordinator收到所有Cohort的ACK响应后,就完成了事务。中断事务如果协调器没有收到参与者发送的ACK响应,则执行中断事务。A.发送中断请求。协调器向所有参与者发送中止请求。B、事务回滚参与者收到中止请求后,使用阶段2记录的undo信息执行事务回滚操作,并在完成回滚后释放所有事务资源。C.反馈结果参与者完成事务回滚后,向协调器发送ACK消息D.中断事务协调器收到参与者反馈的ACK消息后,执行事务的中断。5.3缺点如果进入PreCommit后,Coordinator发送abort请求,假设只有一个Cohort接收并执行abort操作,其他系统状态未知的Cohort会按照3PC选择继续Commit,系统状态为此时的不一致。5.42PC和3PC的区别增加一个query,增加成功概率。为协调器和参与者(Cohort)都设置了超时机制(在2PC中,只有协调器有超时机制,即如果在一定时间内没有收到队列消息,则默认失败).协调者挂掉,参与者等待超时后,默认提交事务。一点点进步。如果参与者异常,协调者也异常,会导致其他参与者提交。在2PC的preparationstage和commitstage之间插入了pre-commitstage,这样3PC就有了三个stage:CanCommit、PreCommit、DoCommit。PreCommit是一个缓冲区,确保每个参与节点的状态在最终提交阶段之前是一致的。6.消息形式的最终一致性采用最终一致性,遵循BASE理论。BASE:全称是BasiclyAvaliable(基本可用)、Softstate(软状态)、Eventuallyconsistent(最终一致性)三个词组的缩写,由eBay的架构师提出。基本可用:在分布式系统环境中,允许牺牲一些不影响主进程的功能的不可用性,对其进行降级,以保证核心服务的正常可用性。软状态:是指在一个事务中,我们允许系统有一个中间状态,而不影响我们的系统。以数据库的主从复制为例,完全允许延迟复制的发生。最终一致:以数据库主从复制为例。主从复制虽然有一点点延迟,但数据最终很快就会一致。分布式事务不能100%解决,只能增加成功概率。两个阶段之间的时间,以毫秒为单位。处理:定时任务补偿。程序或脚本补偿。人工干预。7、TCC解决方案:TCC(Try,Confirm,Cancel),两阶段补偿方案。从名字就可以看出,要实现一个事务,需要定义三个API:资源预占、实际操作资源提交确认、取消占有=回滚。如果最后两个链接有一半失效,记录日志,补偿处理,并通知人工。2PC:是资源层面的分布式事务,一直持有资源锁。如果跨十几个库锁那么多数据库,会导致资源的极度浪费。吞吐量减少。TCC:业务层面的分布式事务,最终一致性,不会一直持有锁。降低了锁的粒度,每操作一个库就释放一次锁。它们都是相对的:如果每天只有一个请求,则使用2PC比TCC性能更高。因为tcc有很多接口调用。这时候2PC就不怕占用资源了,反正就是一个call而已。TCC在高并发场景下有更大的优势。八、消息中间件实现消息队列灵活事务流程图:1.操作payment表,然后在event表中插入一条数据,state为new,放入数据库。这个(1,2,3)操作都在一个事务中,因为都是一个库2.定时任务读取事件表,发送队列,发送成功后,把事件表的状态改成newto(published),监听事件表,往事件表中插入一条数据3.定时任务读库是发布事件表吗?如果是发布的事件表,则更新订单表,更新事件表为processed,这样一个大的事务就拆分成了几个小的事务表。设计:CREATETABLE`t_order_event`(`id`int(16)NOTNULL,`order_type`varchar(32)DEFAULTNULLCOMMENT'事件类型(支付表支付完成,订单表修改状态)',`process`varchar(32)CHARACTERSETutf8mb4COLLATEutf8mb4_0900_ai_ciDEFAULTNULLCOMMENT'eventlink(new,published,processed)',`content`varchar(255)DEFAULTNULLCOMMENT'事件内容,保存事件发生时需要传送的数据',`create_time`datetimeDEFAULTNULLONUPDATECURRENT_TIMESTAMP,`update_time`datetimeDEFAULTNULLONUPDATETIMEPERY)KEY(KEY)引擎=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci;九、seata框架Seata是一个开源的分布式事务解决方案,致力于提供高性能易用的分布式事务服务。Seata将为用户提供AT、TCC、SAGA和XA交易模式,为用户打造一站式的分布式解决方案。官网Api(强烈推荐观看):https://seata.io/zh-cn/docs/overview/what-is-seata.htmlseata下载地址:https://seata.io/zh-cn/blog/下载。html流程图:操作步骤:1.下载seataserverhttps://seata.io/zh-cn/blog/download.html2.修改file.confservice{#transactionservicegroupmapping#Modify,不用改,my_test_tx_group可以随便命名。vgroup_mapping.my_test_tx_group="default"#onlysupportwhenregistry.type=file,pleasedon'tsetmultipleaddresses#此服务的地址default.grouplist="127.0.0.1:8091"#disableseatadisableGlobalTransaction=false}store{##storemode:file、db#修改模式="db"##filestorepropertyfile{##storelocationdirdir="sessionStore"}##databasestoreproperty#db信息修改db{##javax.sql.DataSource的实现,例如DruidDataSource(druid)/BasicDataSource(dbcp)etc.datasource="druid"##mysql/oracle/h2/oceanbaseetc.db-type="mysql"driver-class-name="com.mysql.cj.jdbc.Driver"url="jdbc:mysql://127.0.0.1:3306/seata-server?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai"user="root"password="root"}}3、修改registry.confregistry{#file、nacos、eureka、redis、zk、consul、etcd3、sofa#修改type="eureka"nacos{serverAddr="localhost"namespace=""cluster="default"}#修改eureka{serviceUrl="http://localhost:8761/eureka"application="default"weight="1"}redis{serverAddr="localhost:6379"db="0"}zk{cluster=“默认”serverAddr="127.0.0.1:2181"session.timeout=6000connect.timeout=2000}consul{cluster="default"serverAddr="127.0.0.1:8500"}etcd3{cluster="default"serverAddr="http://localhost:2379"}sofa{serverAddr="127.0.0.1:9603"application="default"region="DEFAULT_ZONE"datacenter="DefaultDataCenter"cluster="default"group="SEATA_GROUP"addressWaitTime="3000"}file{name="file.conf"}}config{#file、nacos、apollo、zk、consul、etcd3type="file"nacos{serverAddr="localhost"namespace=""}consul{serverAddr="127.0.0.1:8500"}阿波罗{app.id="seata-server"apollo.meta="http://192.168.1.204:8801"}zk{serverAddr="127.0.0.1:2181"session.timeout=6000connect.timeout=2000}etcd3{serverAddr="http://localhost:2379"}file{name="file.conf"}}4、创建数据库并建表分支事务表:branchtable全局事务表:globaltable全局锁:lock_table注意:表的结构不能错5.给每个库添加undo_log,用于回滚CREATETABLE`undo_log`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`branch_id`bigint(20)NOTNULL,`xid`varchar(100)CHARACTERSETutf8COLLATEutf8_general_ciNOTNULL,`context`varchar(128)CHARACTERSETutf8COLLATEutf8_general_ciNOTNULL,`rollback_info`longblobNOTNULL,`log_status`NOTNULL,`log_status`NOTNULL,intNULL(11),`log_modified`datetimeNOTNULL,`ext`varchar(100)CHARACTERSETutf8COLLATEutf8_general_ciDEFAULTNULL,PRIMARYKEY(`id`)USINGBTREE,UNIQUEKEY`ux_undo_log`(`xid`,`branch_id`)USINGBTREE)ENGINE=InnoDBDEFAULTCHARSET=utf是分布式事务的引入.有不懂的可以在讨论区留言锡安。小农看到会第一时间回复你。也欢迎大家补充和交流文章中的不足之处。谢谢大家,加油

猜你喜欢