Use Seata to solve distributed transaction problems

Explanation: In a distributed architecture, a request requires multiple microservices to implement. When a request involves multiple microservices, the transaction problem becomes troublesome.

Problem Description

Now there are three services, namely account service, inventory service and order service. To generate an order, it is necessary to ensure whether the inventory of the product is sufficient and the balance of the account is sufficient. The actual operation is three statements (add order, modify account balance, and modify inventory quantity). These three statements form a transaction.

insert image description here

For example, 2.3 Insufficient inventory, modifying the inventory quantity of the database reports an error, at this time, the operation on the account table and the new order operation on the order table have been completed, and the normal business logic is to roll back all the operations related to this business.

Business code

Step1: Initial state of database related tables

insert image description here

Step2: Create order code

insert image description here

Step3: Send request

The requested quantity is greater than the stock quantity

insert image description here

Step4: request error

insert image description here

Inventory is a positive integer, the modified result is less than 0, and the modified error occurs;

insert image description here

Step5: Database status changes

Amount deducted, transaction not fully rolled back

insert image description here

solution

It can be solved using Seata technology . Seata is jointly open sourced by Ant Financial and Alibaba. It is committed to providing high-performance and simple medical distributed transaction services to solve distributed transaction problems.

Seata solves the problem of distributed transactions. Simply speaking, it establishes a TC (Transaction Coordinator) to maintain the status of global and branch transactions, and coordinate global transaction commit/rollback, as shown in the following figure:
insert image description here

The TM (Transaction Manager, Transaction Manager) in the figure can be understood as the scope of the @GlobalTransactional annotation mark, such as the new order method above; RM (Resource Manager, Resource Manager) represents the resources managed by the branch office, which can be simply understood as the database table controlled by the branch office;

The specific implementation of Seata distributed transactions is divided into four modes, the characteristics are as follows:

insert image description here

This article introduces the use of the first two modes in detail. For others, please refer to: http://t.csdn.cn/XZotl

Code

Step 1: Add dependencies

Before using Seata, add dependencies first, because each microservice needs to be added, and I add it to the parent module here.

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    <exclusions>
        <!--版本较低,1.3.0,因此排除-->
        <exclusion>
            <artifactId>seata-spring-boot-starter</artifactId>
            <groupId>io.seata</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!--seata starter 采用1.4.2版本-->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>${seata.version}</version>
</dependency>

Step 2: Set up the configuration file

Set the configuration file of seata, note that each service module needs to be added

seata:
  registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
    # 参考tc服务自己的registry.conf中的配置
    type: nacos
    nacos: # tc
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-tc-server # tc服务在nacos中的服务名称
      cluster: SH
  tx-service-group: seata-demo # 事务组,根据这个获取tc服务的cluster名称
  service:
    vgroup-mapping: # 事务组与TC服务cluster的映射关系
      seata-demo: SH

When adding configuration, you can click "seata" to see if there is a prompt in the idea, and verify whether the dependency is added;

insert image description here

Step 3: Set up the seata configuration file

Set the configuration file of seata on nacos, refer to: http://t.csdn.cn/MOuRz for details;

insert image description here

Step 4: Create a database

Create a special database for seata to record transaction information, and the database name should be consistent with the database name configured on nacos
insert image description here

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;


# 分支事务信息
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table`  (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `status` tinyint(4) NULL DEFAULT NULL,
  `client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime(6) NULL DEFAULT NULL,
  `gmt_modified` datetime(6) NULL DEFAULT NULL,
  PRIMARY KEY (`branch_id`) USING BTREE,
  INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;


# 全局事务信息
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table`  (
  `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `timeout` int(11) NULL DEFAULT NULL,
  `begin_time` bigint(20) NULL DEFAULT NULL,
  `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`xid`) USING BTREE,
  INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
  INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;


# 全局锁信息
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;

Step 5: Create a snapshot table

Create a table (updo_log) to record data snapshot information, which is created in the business database;

insert image description here

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

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;

SET FOREIGN_KEY_CHECKS = 1;

AT mode (default)

schematic diagram

The AT mode is characterized by executing SQL first, and then rolling back the data according to the data snapshot when the branch transaction fails.

insert image description here

Step 1: Add configuration

The mode of adding distributed transactions in each service module is AT:

seata:
  data-source-proxy-mode: AT

Step 2: Modify Notes

On the method that needs to add a transaction, modify the "@Transactional" annotation to "@GlobalTransactional"

insert image description here

Step 3: Start the test

The breakpoint is set on the line of code to determine whether the inventory quantity is greater than the requested quantity, and check the status of each table in the database

insert image description here

The state of each table

insert image description here

Let go of the breakpoint and check the status of each table. You can see that because the execution of the inventory judgment failed, the request quantity is greater than the inventory quantity, the transaction rolls back, and each table rolls back to the initial state, and the distributed transaction problem is solved.

insert image description here

summary

Because the AT mode is to execute the SQL statement first, so when multiple threads are concurrent, if the SQL statement is executed first, and other threads operate the database at this time, data dirty reading occurs. When using AT mode, the problem of data dirty reading needs to be solved.

Seata's solution is to introduce a global lock , that is, to establish a lock for the global transaction. After the branch transaction completes the operation of the database, other threads are not allowed to operate the data at this time. Only after the execution of the global transaction is completed, the lock is released.

(When breaking the point, add a global lock)

insert image description here

(let go of the breakpoint, release the lock)

insert image description here

XA mode

schematic diagram

The structure of XA mode is much clearer than that of AT mode, and it is very simple. The transaction coordinator coordinates and executes it in a unified manner. Once a branch transaction fails to be executed, all branch transactions are rolled back, as follows:

(normal circumstances)
insert image description here

(abnormal situation)

insert image description here

Step 1: Modify the configuration

Modify the configuration of each module, change the mode to XA , and restart all services;

seata:
  registry:
  data-source-proxy-mode: XA

Step 2: Start the test

Similarly, hit the breakpoint on the line of code to judge the inventory, and check the contents of each table;

insert image description here

For the other tables, it can be found that the content of the table is consistent with the XA implementation description ;

insert image description here

The breakpoint is removed, and the program reports an error;
insert image description here

No changes were found in the data, and the XA mode also solved the problem of distributed transactions;

insert image description here

summary

The XA mode needs to lock the database resources and wait for the end of the second phase to release them. The performance is poor, but the implementation is simple, and there is no need to consider the problem of dirty data reading.

Summarize

Seata can solve distributed transaction problems and provides four modes, AT is the default mode;

Guess you like

Origin blog.csdn.net/qq_42108331/article/details/131867380