

目录
项目的业务逻辑层主要实现具体的跨库转账的业务逻辑,由于具体 的XA跨库分布式事务是由Atomikos框架内部实现的,因此在业务逻 辑层处理跨库转账的逻辑时,就像操作本地数据库一样简单。
创建UserAccount类
- @Data
- @TableName("user_account")
- @AllArgsConstructor
- @NoArgsConstructor
- public class UserAccount implements Serializable {
- private static final long serialVersionUID = 6909533252826367496L;
- /**
- * 账户编号
- */
- @TableId
- private String accountNo;
- /**
- * 账户名称
- */
- private String accountName;
- /**
- * 账户余额
- */
- private BigDecimal accountBalance;
- }
创建UserAccountService接口
- public interface UserAccountService {
- /**
- * 跨库转账
- * @param sourceAccountNo 转出账户
- * @param targetSourceNo 转入账户
- * @param bigDecimal 金额
- */
- void transferAccounts(String sourceAccountNo, String targetSourceNo,BigDecimal transferAmount);
- }
实现UserAccountService接口
- package com.tong.service.impl;
- import com.tong.entity.UserAccount;
- import com.tong.mapper1.UserAccountMapper1;
- import com.tong.mapper2.UserAccountMapper2;
- import com.tong.service.IUserAccountService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import java.math.BigDecimal;
- /**
- *
- * 服务实现类
- *
- *
- * @author itbaizhan
- * @since 05-13
- */
- @Service
- public class UserAccountServiceImpl implements IUserAccountService {
- @Autowired
- private UserAccountMapper1 userAccountMapper1;
- @Autowired
- private UserAccountMapper2 userAccountMapper2;
- /**
- * 跨库转账
- * @param sourceAccountNo 源账户
- * @param targetSourceNo 目标账户
- * @param bigDecimal 金额
- */
- @Transactional
- @Override
- public void transofer(String sourceAccountNo, String targetSourceNo, BigDecimal bigDecimal) {
- // 1. 查询原账户
- UserAccount sourceUserAccount = userAccountMapper1.selectById(sourceAccountNo);
- // 2. 查询目标账户
- UserAccount targetUserAccount = userAccountMapper2.selectById(targetSourceNo);
- // 3. 判断转入账户和转出账户是否为空
- if (sourceAccountNo != null && targetUserAccount != null){
- // 4. 判断转出账户是否余额不足
- if (sourceUserAccount.getAccountBalance().compareTo(bigDecimal) < 0){
- throw new RuntimeException("余额不足");
- }
- // 5.更新金额
- sourceUserAccount.setAccountBalance(sourceUserAccount.getAccountBalance().subtract(bigDecimal));
- // 6.张三账户减金额
- userAccountMapper1.updateById(sourceUserAccount);
- System.out.println(10/0);
- // 7.更新金额
- targetUserAccount.setAccountBalance(targetUserAccount.getAccountBalance().add(bigDecimal));
- // 8.张三账户减金额
- userAccountMapper2.updateById(targetUserAccount);
- }
- }
- }

为什么会出现BASE理论
CAP 理论表明,对于一个分布式系统而言,它是无法同时满足 Consistency(强一致性)、Availability(可用性) 和 Partition tolerance(分区容忍性) 这三个条件的,最多只能满足其中两个。


简介
BASE 理论起源于 2008 年, 由 eBay 的架构师 Dan Pritchett 在 ACM 上发表。
什么是BASE理论
BASE 是 Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性) 三个短语的缩写。
核心思想:
既是无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
基本可用(Basically Available)
基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。允许损失部分可用性。但是,这绝不等价于 系统不可用。
软状态(Soft State)
软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。即允许系统在多个不同节点的数据副本存在数据延时。

注意:
用户在商城下单时,因网络超时等因素,订单处于“支付中”的状 态,待数据最终一致后状态将变更为“关闭”或“成功”状态。
最终一致性(Eventual Consistency)
最终一致性是指系统中的所有数据副本经过一定时间后,最终能够 达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。
总结
ACID 是数据库事务完整性的理论,CAP 是分布式系统设计理论, BASE是 CAP 理论中 AP 方案的延伸。符合Base理论的事务可以称为柔性事务。

什么是最终一致性事务
强一致性分布式事务解决方案要求参与事务的各个节点的数据时刻 保持一致,查询任意节点的数据都能得到最新的数据结果。这就导 致在分布式场景,尤其是高并发场景下,系统的性能受到影响。而 最终一致性分布式事务解决方案并不要求参与事务的各节点数据时刻保持一致,允许其存在中间状态,只要一段时间后,能够达到数据的最终一致状态即可。
典型方案
为了解决分布式、高并发场景下系统的性能问题,业界基于Base理论提出了最终一致性分布式事务解决方案。

适用场景


优缺点
最终一致性分布式事务解决方案的优点:

最终一致性分布式事务解决方案的缺点:


概念
TCC(Try-Confirm-Cancel)又称补偿事务。
TCC核心思想
TCC分布式事务最核心的思想就是在应用层将一个完整的事务操作分为三个阶段。在某种程度上讲,TCC是一种资源,实现了Try、 Confirm、Cancel三个操作接口。

Try阶段是准备执行业务的阶段,在这个阶段尝试执行业务。


Confirm阶段
Confirm阶段是确认执行业务的阶段,在这个阶段确认执行的业务。

Cancel阶段
Cancel阶段取消执行业务。



概述
Hmily是一款高性能,零侵入,金融级分布式事务解决方案,目前 主要提供柔性事务的支持,包含 TCC , TAC (自动生成回滚SQL) 方案, 未来还会支持 XA 等方案。


案例程序分为3个部分
项目公共模块、转出银行微服务和转入银行微服务。转出银行微服 务和转入银行微服务引用项目的公共模块,转出银行微服务作为 TCC分布式事务中的事务发起方,转入银行微服务作为TCC分布式事 务中的事务被动方。

框架选择

数据库表设计
在模拟跨行转账的业务场景中,核心服务包括转出银行微服务和转入银行微服务,对应的数据库包括转出银行数据库和转入银行数据库。
user_account账户数据表
| 字段名称 | 字段类型 | 字段名称 |
| account_no | varchar(64) | 账户编号 |
| account_name | varchar(64) | 账户名称 |
| account_balance | decimal(10,2) | 账户余额 |
| fransfer_amount | decimal(10,2) | 转账金额,用于锁定资源 |
try_log记录表

confirm_log记录表

cancel_log记录表

接下来,在192.168.66.100服务器的MySQL命令行执行如下命令创建转出银行数据库和数据表。
- SET NAMES utf8mb4;
- SET FOREIGN_KEY_CHECKS = 0;
- -- ----------------------------
- -- Table structure for cancel_log
- -- ----------------------------
- DROP TABLE IF EXISTS `cancel_log`;
- CREATE TABLE `cancel_log` (
- `tx_no` varchar(64) CHARACTER SET utf8mb4
- COLLATE utf8mb4_general_ci NOT NULL COMMENT '全局事务编号',
- `create_time` datetime(0) NULL DEFAULT NULL
- COMMENT '创建时间',
- PRIMARY KEY (`tx_no`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = 'Cancel
- 阶段执行的日志记录' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of cancel_log
- -- ----------------------------
- -- ----------------------------
- -- Table structure for confirm_log
- -- ----------------------------
- DROP TABLE IF EXISTS `confirm_log`;
- CREATE TABLE `confirm_log` (
- `tx_no` varchar(64) CHARACTER SET utf8mb4
- COLLATE utf8mb4_general_ci NOT NULL COMMENT '全局事务编号',
- `create_time` datetime(0) NULL DEFAULT NULL
- COMMENT '创建时间',
- PRIMARY KEY (`tx_no`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = 'Confirm
- 阶段执行的日志记录' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of confirm_log
- -- ----------------------------
- -- ----------------------------
- -- Table structure for try_log
- -- ----------------------------
- DROP TABLE IF EXISTS `try_log`;
- CREATE TABLE `try_log` (
- `tx_no` varchar(64) CHARACTER SET utf8mb4
- COLLATE utf8mb4_general_ci NOT NULL COMMENT '全局事务编号',
- `create_time` datetime(0) NULL DEFAULT
- CURRENT_TIMESTAMP(0) COMMENT '创建时间'
- ,
- PRIMARY KEY (`tx_no`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = 'Try阶段
- 执行的日志记录' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of try_log
- -- ----------------------------
- -- ----------------------------
- -- Table structure for user_account
- -- ----------------------------
- DROP TABLE IF EXISTS `user_account`;
- CREATE TABLE `user_account` (
- `account_no` varchar(64) CHARACTER SET
- utf8mb4 COLLATE utf8mb4_general_ci NOT NULL
- COMMENT '账户编号',
- `account_name` varchar(50) CHARACTER SET
- utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT
- NULL COMMENT '账户名称',
- `account_balance` decimal(10, 2) NULL DEFAULT
- NULL COMMENT '账户余额',
- `transfer_amount` decimal(10, 2) NULL DEFAULT
- NULL COMMENT '转账金额',
- PRIMARY KEY (`account_no`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = '账户信
- 息' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of user_account
- -- ----------------------------
- INSERT INTO `user_account` VALUES ('1001',
- '张三', 10000.00, 0.00);
- SET FOREIGN_KEY_CHECKS = 1;
在192.168.66.100服务器的MySQL命令行执行如下命令创建转入银行数据库和数据表。
- SET NAMES utf8mb4;
- SET FOREIGN_KEY_CHECKS = 0;
- -- ----------------------------
- -- Table structure for cancel_log
- -- ----------------------------
- DROP TABLE IF EXISTS `cancel_log`;
- CREATE TABLE `cancel_log` (
- `tx_no` varchar(64) CHARACTER SET utf8mb4
- COLLATE utf8mb4_general_ci NOT NULL COMMENT '全局事务编号',
- `create_time` datetime(0) NULL DEFAULT NULL
- COMMENT '创建时间',
- PRIMARY KEY (`tx_no`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = 'Cancel阶段执行的日志记录' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of cancel_log
- -- ----------------------------
- -- ----------------------------
- -- Table structure for confirm_log
- -- ----------------------------
- DROP TABLE IF EXISTS `confirm_log`;
- CREATE TABLE `confirm_log` (
- `tx_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '全局事务编号',
- `create_time` datetime(0) NULL DEFAULT NULL
- COMMENT '创建时间',
- PRIMARY KEY (`tx_no`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = 'Confirm
- 阶段执行的日志记录' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of confirm_log
- -- ----------------------------
- -- ----------------------------
- -- Table structure for try_log
- -- ----------------------------
- DROP TABLE IF EXISTS `try_log`;
- CREATE TABLE `try_log` (
- `tx_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '全
- 局事务编号',
- `create_time` datetime(0) NULL DEFAULT NULL
- COMMENT '创建时间',
- PRIMARY KEY (`tx_no`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = 'Try阶段
- 执行的日志记录' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of try_log
- -- ----------------------------
- -- ----------------------------
- -- Table structure for user_account
- -- ----------------------------
- DROP TABLE IF EXISTS `user_account`;
- CREATE TABLE `user_account` (
- `account_no` varchar(64) CHARACTER SET
- utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户编号',
- `account_name` varchar(50) CHARACTER SET
- utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT
- NULL COMMENT '账户名称',
- `account_balance` decimal(10, 2) NULL DEFAULT
- NULL COMMENT '账户余额',
- `transfer_amount` decimal(10, 2) NULL DEFAULT
- NULL COMMENT '转账金额',
- PRIMARY KEY (`account_no`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_general_ci COMMENT = '账户信息' ROW_FORMAT = Dynamic;
- -- ----------------------------
- -- Records of user_account
- -- ----------------------------
- INSERT INTO `user_account` VALUES ('1002','李四', 10000.00, 0.00);
- SET FOREIGN_KEY_CHECKS = 1;