临沂手机网站信息推广技术公司电话,网校网站模板,深圳市住房和建设局网站住房保障,做网站怎么带流量一、事务模式
1.1、XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理#xff08;DTP#xff0c;Distributed Transaction Processing#xff09;标准#xff0c;XA 规范 描述了全局的TM与局部的RM之间的接口#xff0c;几乎所有主流的数据库都对 XA 规范 提供了支持。…一、事务模式
1.1、XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理DTPDistributed Transaction Processing标准XA 规范 描述了全局的TM与局部的RM之间的接口几乎所有主流的数据库都对 XA 规范 提供了支持。 1.1.1、两阶段提交
XA是规范目前主流数据库都实现了这种规范实现的原理都是基于两阶段提交。
正常情况 异常情况 一阶段 事务协调者通知每个事物参与者执行本地事务 本地事务执行完成后报告事务执行状态给事务协调者此时事务不提交继续持有数据库锁
二阶段 事务协调者基于一阶段的报告来判断下一步操作 如果一阶段都成功则通知所有事务参与者提交事务 如果一阶段任意一个参与者失败则通知所有事务参与者回滚事务 1.1.2、Seata的XA模型
Seata对原始的XA模式做了简单的封装和改造以适应自己的事务模型基本架构如图 RM一阶段的工作
① 注册分支事务到TC
② 执行分支业务sql但不提交
③ 报告执行状态到TC
TC二阶段的工作 TC检测各分支事务执行状态 a.如果都成功通知所有RM提交事务 b.如果有失败通知所有RM回滚事务
RM二阶段的工作 接收TC指令提交或回滚事务 1.1.3、优缺点
XA模式的优点是什么 事务的强一致性满足ACID原则。 常用数据库都支持实现简单并且没有代码侵入
XA模式的缺点是什么 因为一阶段需要锁定数据库资源等待二阶段结束才释放性能较差 依赖关系型数据库实现事务 1.1.4、实现XA模式
Seata的starter已经完成了XA模式的自动装配实现非常简单步骤如下
1修改application.yml文件每个参与事务的微服务开启XA模式
seata:data-source-proxy-mode: XA 2给发起全局事务的入口方法添加GlobalTransactional注解: 1.2、AT模式
AT模式同样是分阶段提交的事务模型不过缺弥补了XA模型中资源锁定周期过长的缺陷。 1.2.1、Seata的AT模型
基本流程图 阶段一RM的工作 注册分支事务 记录undo-log数据快照 执行业务sql并提交 报告事务状态
阶段二提交时RM的工作 删除undo-log即可
阶段二回滚时RM的工作 根据undo-log恢复数据到更新前 1.2.2、流程梳理
我们用一个真实的业务来梳理下AT模式的原理。
比如现在又一个数据库表记录用户余额
idmoney1100
其中一个分支业务要执行的SQL为
update tb_account set money money - 10 where id 1
AT模式下当前分支事务执行流程如下
一阶段
TM发起并注册全局事务到TCTM调用分支事务分支事务准备执行业务SQLRM拦截业务SQL根据where条件查询原始数据形成快照。 {id: 1, money: 100
} RM执行业务SQL提交本地事务释放数据库锁。此时 money 90RM报告本地事务状态给TC 二阶段
TM通知TC事务结束TC检查分支事务状态 a、如果都成功则立即删除快照 b、如果有分支事务失败需要回滚。读取快照数据{id: 1, money: 100}将快照恢复到数据库。此时数据库再次恢复为100
流程图 1.2.3、AT与XA的区别
简述AT模式与XA模式最大的区别是什么 XA模式一阶段不提交事务锁定资源AT模式一阶段直接提交不锁定资源。 XA模式依赖数据库机制实现回滚AT模式利用数据快照实现数据回滚。 XA模式强一致AT模式最终一致 1.2.4、脏写问题
在多线程并发访问AT模式的分布式事务时有可能出现脏写问题如图 解决思路就是引入了全局锁的概念。在释放DB锁之前先拿到全局锁。避免同一时刻有另外一个事务来操作当前数据。 1.2.5、优缺点
AT模式的优点 一阶段完成直接提交事务释放数据库资源性能比较好 利用全局锁实现读写隔离 没有代码侵入框架自动完成回滚和提交
AT模式的缺点 两阶段之间属于软状态属于最终一致 框架的快照功能会影响性能但比XA模式要好很多 1.2.6、实现AT模式
AT模式中的快照生成、回滚等动作都是由框架自动完成没有任何代码侵入因此实现非常简单。
只不过AT模式需要一个表来记录全局锁、另一张表来记录数据快照undo_log。
1、导入数据库表记录全局锁
其中lock_table导入到TC服务关联的数据库undo_log表导入到微服务关联的数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS 0;-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS undo_log;
CREATE TABLE undo_log (branch_id bigint(20) NOT NULL COMMENT branch transaction id,xid varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT global transaction id,context varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT undo_log context,such as serialization,rollback_info longblob NOT NULL COMMENT rollback info,log_status int(11) NOT NULL COMMENT 0:normal status,1:defense status,log_created datetime(6) NOT NULL COMMENT create datetime,log_modified datetime(6) NOT NULL COMMENT modify datetime,UNIQUE INDEX ux_undo_log(xid, branch_id) USING BTREE
) ENGINE InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT AT transaction mode undo table ROW_FORMAT Compact;-- ----------------------------
-- Records of undo_log
-- ------------------------------ ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS lock_table;
CREATE TABLE lock_table (row_key varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,xid varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,transaction_id bigint(20) NULL DEFAULT NULL,branch_id bigint(20) NOT NULL,resource_id varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,table_name varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,pk varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,gmt_create datetime NULL DEFAULT NULL,gmt_modified datetime NULL DEFAULT NULL,PRIMARY KEY (row_key) USING BTREE,INDEX idx_branch_id(branch_id) USING BTREE
) ENGINE InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT Compact;SET FOREIGN_KEY_CHECKS 1; 2、修改application.yml文件将事务模式修改为AT模式即可
seata:data-source-proxy-mode: AT # 默认就是AT 1.3、TCC模式
TCC模式与AT模式非常相似每阶段都是独立事务不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法 Try资源的检测和预留 Confirm完成资源操作业务要求 Try 成功 Confirm 一定要能成功。 Cancel预留资源释放可以理解为try的反向操作。 1.3.1、流程分析
举例一个扣减用户余额的业务。假设账户A原来余额是100需要余额扣减30元。 阶段一 Try 检查余额是否充足如果充足则冻结金额增加30元可用余额扣除30 初始余额 余额充足可以冻结 此时总金额 冻结金额 可用金额数量依然是100不变。事务直接提交无需等待其它事务。 阶段二Confirm)假如要提交Confirm则冻结金额扣减30 确认可以提交不过之前可用金额已经扣减过了这里只要清除冻结金额就好了 此时总金额 冻结金额 可用金额 0 70 70元 阶段二(Canncel)如果要回滚Cancel则冻结金额扣减30可用余额增加30 需要回滚那么就要释放冻结金额恢复可用金额 1.3.2、Seata的TCC模型
Seata中的TCC模型依然延续之前的事务架构如图 1.3.3、优缺点
TCC模式的每个阶段是做什么的 Try资源检查和预留 Confirm业务执行和提交 Cancel预留资源的释放
TCC的优点是什么 一阶段完成直接提交事务释放数据库资源性能好 相比AT模型无需生成快照无需使用全局锁性能最强 不依赖数据库事务而是依赖补偿操作可以用于非事务型数据库
TCC的缺点是什么 有代码侵入需要人为编写try、Confirm和Cancel接口太麻烦 软状态事务是最终一致 需要考虑Confirm和Cancel的失败情况做好幂等处理 1.3.4、事务悬挂和空回滚 空回滚 当某分支事务的try阶段阻塞时可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作这时cancel不能做回滚就是空回滚。 如图 执行cancel操作时应当判断try是否已经执行如果尚未执行则应该空回滚。 业务悬挂 对于已经空回滚的业务之前被阻塞的try操作恢复继续执行try就永远不可能confirm或cancel 事务一直处于中间状态这就是业务悬挂。 执行try操作时应当判断cancel是否已经执行过了如果已经执行应当阻止空回滚后的try操作避免悬挂 1.3.5、实现TCC模式
解决空回滚和业务悬挂问题必须要记录当前事务状态是在try、还是cancel 1、思路分析 这里我们定义一张表 CREATE TABLE account_freeze_tbl (xid varchar(128) NOT NULL,user_id varchar(255) DEFAULT NULL COMMENT 用户id,freeze_money int(11) unsigned DEFAULT 0 COMMENT 冻结金额,state int(1) DEFAULT NULL COMMENT 事务状态0:try1:confirm2:cancel,PRIMARY KEY (xid) USING BTREE
) ENGINEInnoDB DEFAULT CHARSETutf8 ROW_FORMATCOMPACT;其中 xid是全局事务id freeze_money用来记录用户冻结金额 state用来记录事务状态 那此时我们的业务开怎么做呢 Try业务 记录冻结金额和事务状态到account_freeze表 扣减account表可用金额 Confirm业务 根据xid删除account_freeze表的冻结记录 Cancel业务 修改account_freeze表冻结金额为0state为2 修改account表恢复可用金额 如何判断是否空回滚 cancel业务中根据xid查询account_freeze如果为null则说明try还没做需要空回滚 如何避免业务悬挂 try业务中根据xid查询account_freeze 如果已经存在则证明Cancel已经执行拒绝执行try业务 接下来我们改造account-service利用TCC实现余额扣减功能。 2、声明TCC接口 TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明 我们在account-service项目中的cn.itcast.account.service包中新建一个接口声明TCC三个接口 LocalTCC
public interface AccountTCCService {TwoPhaseBusinessAction(name deduct, commitMethod confirm, rollbackMethod cancel)void deduct(BusinessActionContextParameter(paramName userId) String userId,BusinessActionContextParameter(paramName money)int money);boolean confirm(BusinessActionContext ctx);boolean cancel(BusinessActionContext ctx);
} 3、编写实现类 在account-service服务中的cn.itcast.account.service.impl包下新建一个类实现TCC业务 Service
Slf4j
public class AccountTCCServiceImpl implements AccountTCCService {Autowiredprivate AccountMapper accountMapper;Autowiredprivate AccountFreezeMapper freezeMapper;OverrideTransactionalpublic void deduct(String userId, int money) {// 0.获取事务idString xid RootContext.getXID();// 1.扣减可用余额accountMapper.deduct(userId, money);// 2.记录冻结金额事务状态AccountFreeze freeze new AccountFreeze();freeze.setUserId(userId);freeze.setFreezeMoney(money);freeze.setState(AccountFreeze.State.TRY);freeze.setXid(xid);freezeMapper.insert(freeze);}Overridepublic boolean confirm(BusinessActionContext ctx) {// 1.获取事务idString xid ctx.getXid();// 2.根据id删除冻结记录int count freezeMapper.deleteById(xid);return count 1;}Overridepublic boolean cancel(BusinessActionContext ctx) {// 0.查询冻结记录String xid ctx.getXid();AccountFreeze freeze freezeMapper.selectById(xid);// 1.恢复可用余额accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());// 2.将冻结金额清零状态改为CANCELfreeze.setFreezeMoney(0);freeze.setState(AccountFreeze.State.CANCEL);int count freezeMapper.updateById(freeze);return count 1;}
} 1.4、SAGA模式
Saga 模式是 Seata 即将开源的长事务解决方案将由蚂蚁金服主要贡献。
其理论基础是Hector Kenneth 在1987年发表的论文Sagas。
Seata官网对于Saga的指南Seata Saga 模式 1.4.1、原理
在 Saga 模式下分布式事务内有多个参与者每一个参与者都是一个冲正补偿服务需要用户根据业务场景实现其正向操作和逆向回滚操作。
分布式事务执行过程中依次执行各参与者的正向操作如果所有正向操作均执行成功那么分布式事务提交。如果任何一个正向操作执行失败那么分布式事务会去退回去执行前面各参与者的逆向回滚操作回滚已提交的参与者使分布式事务回到初始状态。 Saga也分为两个阶段 一阶段直接提交本地事务 二阶段成功则什么都不做失败则通过编写补偿业务来回滚 1.4.2、优缺点
优点 事务参与者可以基于事件驱动实现异步调用吞吐高 一阶段直接提交事务无锁性能好 不用编写TCC中的三个阶段实现简单
缺点 软状态持续时间不确定时效性差 没有锁没有事务隔离会有脏写 1.5、四种模式对比
我们从以下几个方面来对比四种实现 一致性能否保证事务的一致性强一致还是最终一致 隔离性事务之间的隔离性如何 代码侵入是否需要对业务代码改造 性能有无性能损耗 场景常见的业务场景
如图 二、高可用
Seata的TC服务作为分布式事务核心一定要保证集群的高可用性。 2.1、高可用架构模型
搭建TC服务集群非常简单启动多个TC服务注册到nacos即可。
但集群并不能确保100%安全万一集群所在机房故障怎么办所以如果要求较高一般都会做异地多机房容灾。
比如一个TC集群在上海另一个TC集群在杭州 微服务基于事务组tx-service-group)与TC集群的映射关系来查找当前应该使用哪个TC集群。当SH集群故障时只需要将vgroup-mapping中的映射关系改成HZ。则所有微服务就会切换到HZ的TC集群了。 2.2、实现高可用
1、模拟异地容灾的TC集群 计划启动两台seata的tc服务节点 节点名称ip地址端口号集群名称seata127.0.0.18091SHseata2127.0.0.18092HZ 之前我们已经启动了一台seata服务端口是8091集群名为SH。 现在将seata目录复制一份起名为seata2 修改seata2/conf/registry.conf内容如下 registry {# tc服务的注册中心类这里选择nacos也可以是eureka、zookeeper等type nacosnacos {# seata tc 服务注册到 nacos的服务名称可以自定义application seata-tc-serverserverAddr 127.0.0.1:8848group DEFAULT_GROUPnamespace cluster HZusername nacospassword nacos}
}config {# 读取tc服务端的配置文件的方式这里是从nacos配置中心读取这样如果tc是集群可以共享配置type nacos# 配置nacos地址等信息nacos {serverAddr 127.0.0.1:8848namespace group SEATA_GROUPusername nacospassword nacosdataId seataServer.properties}
} 进入seata2/bin目录然后运行命令 seata-server.bat -p 8092 打开nacos控制台查看服务列表 点进详情查看 2、将事务组映射配置到nacos 接下来我们需要将tx-service-group与cluster的映射关系都配置到nacos配置中心。 新建一个配置 配置的内容如下 # 事务组映射关系
service.vgroupMapping.seata-demoSHservice.enableDegradefalse
service.disableGlobalTransactionfalse
# 与TC服务的通信配置
transport.typeTCP
transport.serverNIO
transport.heartbeattrue
transport.enableClientBatchSendRequestfalse
transport.threadFactory.bossThreadPrefixNettyBoss
transport.threadFactory.workerThreadPrefixNettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefixNettyServerBizHandler
transport.threadFactory.shareBossWorkerfalse
transport.threadFactory.clientSelectorThreadPrefixNettyClientSelector
transport.threadFactory.clientSelectorThreadSize1
transport.threadFactory.clientWorkerThreadPrefixNettyClientWorkerThread
transport.threadFactory.bossThreadSize1
transport.threadFactory.workerThreadSizedefault
transport.shutdown.wait3
# RM配置
client.rm.asyncCommitBufferLimit10000
client.rm.lock.retryInterval10
client.rm.lock.retryTimes30
client.rm.lock.retryPolicyBranchRollbackOnConflicttrue
client.rm.reportRetryCount5
client.rm.tableMetaCheckEnablefalse
client.rm.tableMetaCheckerInterval60000
client.rm.sqlParserTypedruid
client.rm.reportSuccessEnablefalse
client.rm.sagaBranchRegisterEnablefalse
# TM配置
client.tm.commitRetryCount5
client.tm.rollbackRetryCount5
client.tm.defaultGlobalTransactionTimeout60000
client.tm.degradeCheckfalse
client.tm.degradeCheckAllowTimes10
client.tm.degradeCheckPeriod2000# undo日志配置
client.undo.dataValidationtrue
client.undo.logSerializationjackson
client.undo.onlyCareUpdateColumnstrue
client.undo.logTableundo_log
client.undo.compress.enabletrue
client.undo.compress.typezip
client.undo.compress.threshold64k
client.log.exceptionRate100 3、微服务读取nacos配置 接下来需要修改每一个微服务的application.yml文件让微服务读取nacos中的client.properties文件 seata:config:type: nacosnacos:server-addr: 127.0.0.1:8848username: nacospassword: nacosgroup: SEATA_GROUPdata-id: client.properties 重启微服务现在微服务到底是连接tc的SH集群还是tc的HZ集群都统一由nacos的client.properties来决定了。