** Note: ** Most of this article is excerpted from the seATA website. The purpose of this article is to summarize some of the contents of the SEATA website for future review.
What is SEATA
Seata is an open source distributed transaction solution dedicated to providing high performance and easy to use distributed transaction services. Seata will provide users with AT, TCC, SAGA and XA transaction modes to create a one-stop distributed solution for users. Among them, THE AT mode is mainly promoted by Seata and is implemented based on two-stage commit.
Terms:
-
TC Transaction Coordinator (TC) Transaction Coordinator
Maintains the state of global and branch transactions and drives global transaction commit or rollback.
-
TM (Transaction Manager) – Transaction Manager
Define the scope of a global transaction: start, commit, or roll back the global transaction.
-
RM Resource Manager (RM) – Resource Manager
Manage resources for branch transaction processing, talk to TCS to register branch transactions and report status of branch transactions, and drive commit or rollback of branch transactions.
Ii. Introduction of AT mode
In the AT mode, each service library must have an undo_log table, which stores the mirrored data before and after service data execution.
1. Prerequisites
- Based on a relational database that supports local ACID transactions.
- Java applications, access the database through JDBC.
2. Overall mechanism
Evolution of the two-phase commit protocol:
- Phase one: Business data and rollback log records are committed in the same local transaction, freeing local locks and connection resources.
- Stage 2:
- Commits are asynchronous and done very quickly.
- Rollback is compensated in reverse by the rollback log of one phase.
3. Implementation of read and write isolation
1. Write isolation
- Make sure you get the global lock before committing a phase 1 local transaction.
- Unable to commit local transaction because global lock could not be obtained.
- Attempts to acquire the global lock are limited to a certain range, beyond which the local transaction is abandoned and rolled back to release the local lock.
To illustrate:
Tx1, tx2, tx1, tx2, tx1, tx2;
Tx1 start, open local transaction, get local lock, update operation m = 1000-100 = 900. Before a local transaction is committed, the global lock of the record is obtained. The local commit releases the local lock. Tx2 start, open local transaction, get local lock, update operation m = 900-100 = 800. Before the local transaction is committed, the global lock of the record is held by TX1. Tx2 needs to retry to wait for the global lock.
Tx1 two-phase global commit, release global lock. Tx2 takes the global lock and commits the local transaction.
If tx1 has a two-phase global rollback, then TX1 needs to re-acquire the local lock of the data and perform the reverse compensation update operation to achieve the rollback of the branch.
At this point, if TX2 is still waiting for a global lock for that data and holds a local lock, the branch rollback of TX1 will fail. The branch rollback will continue to retry until tx2’s global lock times out, the global lock is abandoned and the local transaction is rolled back to release the local lock, and the branch rollback of TX1 finally succeeds.
Because the global lock is held by TX1 until tx1 ends, dirty writes do not occur.
2. Read isolation
The default global isolation level for Seata (AT mode) is Read Uncommitted, based on the database local transaction isolation level Read Committed or above.
If the application is applied in certain scenarios, it must require that the global read has been committed. Currently, Seata is brokered through SELECT FOR UPDATE statements.
The execution of the SELECT FOR UPDATE statement requests a global lock, and if the global lock is held by another transaction, the local lock is released (roll back the local execution of the SELECT FOR UPDATE statement) and retry. In this process, the query is held by the block and is not returned until the global lock is taken, i.e. the relevant data read is committed.
FOR overall performance, Seata’s current approach does not proxy all SELECT statements, only SELECT statements FOR UPDATE.
Third, transaction grouping
1. What are transaction groups?
Transaction groups are seATA’s resource logic, similar to service instances. My_test_tx_group in file.conf is a transaction group. It is possible to register different microservices to different groups.
2. How to find the back-end cluster through transaction grouping?
- First, transaction groups are configured in the program (txServiceGroup parameter to the GlobalTransactionScanner constructor).
- The application will look for service.vgroupMapping through the user-configured configuration center.[Transaction group configuration item], the value of the configuration item is the TC cluster name
- Get the cluster name program through a certain prefix suffix + cluster name to construct the service name, the service name of each configuration center is different
- Get the service name and go to the corresponding registry to pull the service list of the corresponding service name to get the real TC service list of the backend
3. Why not take the service name directly?
There is an additional layer of configuration to get transaction groups to the mapping cluster. In this way, the transaction group can be used as the logical isolation unit of resources. When a cluster fails, you can perform a quick failover by switching only the corresponding group. The fault can be reduced to the service level, but the premise is that you have enough server clusters.
4. Examples of grouping things
1. Remote multi-room Dr Of TC
- Assume that the TC cluster is deployed in two machine rooms: Guangzhou machine room (primary) and Shanghai machine room (secondary)
- Complete microservices Architecture project: projectA
- ProjectA includes serviceD serviceD: serviceA, serviceB, serviceC and serviceD serviceD
Where, the transaction group tx-transaction-group of all projectA microservices is set to: projectA. ProjectA normally uses The TC cluster of Guangzhou (master).
In normal cases, the client configuration is as follows:
seata.tx-service-group=projectA
seata.service.vgroup-mapping.projectA=Guangzhou
Copy the code
If the whole Guangzhou cluster group is down at this time, or projectA cannot communicate with The Guangzhou machine room temporarily due to network reasons, we will change the Guangzhou cluster group in the configuration center to Shanghai as follows:
seata.service.vgroup-mapping.projectA=Shanghai
Copy the code
And pushed to the microservices to complete dynamic TC cluster switching across projectA.
2. Access to multiple applications in a single environment
- Suppose you have a whole set of SEATA clusters in your development environment (or pre-launch/production)
- The SEATA cluster serves different microservices architecture projects projectA, projectB, and projectC
- ProjectA, projectB, and projectC are relatively independent
We pair-grouped the six instances in the SeATA cluster to serve projectA, projectB, and projectC, so the configuration on the SeATa-Server side is as follows (using the NACOS registry as an example) :
registry { type = "nacos" loadBalance = "RandomLoadBalance" loadBalanceVirtualNodes = 10 nacos { application = "Seta-server" serverAddr = "127.0.0.1:8848" group = "DEFAULT_GROUP" Namespace = "8f11AEB1-5042-461B-b88B-d47a7f7e01C0" "serverAddr = "127.0.0.1:8848" group = "DEFAULT_GROUP" Namespace =" 8F11AEB1-5042-461B-b88B-d47a7F7e01C0" Mysql > configure project-b-group/project-c-group cluster = "project-a-group" username = "username" password = "password" } }Copy the code
The configuration of the client is as follows:
seata.tx-service-group=projectA
# configure project-b-group/project-c-group for projectB and projectC
seata.service.vgroup-mapping.projectA=project-a-group
Copy the code
After the configuration is complete, TCS in the corresponding transaction group serve the application separately. The overall deployment diagram is as follows:
3. Fine control of client
- Suppose there is a Seata cluster now, with the Guangzhou machine room instance running on a higher performance machine and the Shanghai cluster running on a lower performance machine
- Existing Microservices Architecture project projectA includes Microservices ServiceA, ServiceB, ServiceC, and ServiceD ServiceD
- ServiceD ServiceD volume was small, while the remaining ServiceD ServiceD volume was large
In this case, ServiceD microservices can be transferred to the Shanghai cluster, while high-performance servers are replaced by other high-volume microservices. (Vice versa. If a single ServiceD microservice has high volume, we can create a cluster with superior performance for ServiceD microservices. And point the virtual group of the client to the cluster, whose ultimate purpose is to ensure the service availability in the peak traffic.)
4. Seata pre-delivery and production isolation
- In most cases, the pre-release environment and production environment will use the same set of databases. Based on this condition, the pre-issued TC cluster and the production TC cluster must use the same database to ensure the effect of global transactions (that is, the production TC cluster and the pre-issued TC cluster use the same LOCK table and use different Branch_table and global_table).
- We remember that the branch table and global table used in production are global_table and branch_table respectively. Global_table_pre, branch_table_pre
- Pre-dispatch and production share lock_table
In this case, the file.conf configuration of seata-server is as follows
store { mode = "db" db { datasource = "druid" dbType = "mysql" driverClassName = "com.mysql.jdbc.Driver" url = "JDBC: mysql: / / 127.0.0.1:3306 / seata" user = "username", "=" password "minConn globalTable = = 5 maxConn = 100 "Global_table" ----> Pre-send as "global_table_pre" branchTable = "branch_table" ----> Pre-send as "branch_table_pre" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } }Copy the code
The registry. Conf configuration of seata-Server is as follows (using NACOS as an example)
registry { type = "nacos" loadBalance = "RandomLoadBalance" loadBalanceVirtualNodes = 10 nacos { application = "Seta-server" serverAddr = "127.0.0.1:8848" group = "DEFAULT_GROUP" Namespace = "8f11AEB1-5042-461B-b88B-d47a7f7e01C0" "serverAddr = "127.0.0.1:8848" group = "DEFAULT_GROUP" Namespace =" 8F11AEB1-5042-461B-b88B-d47a7F7e01C0" Cluster = "pre-product" --> "product" username = "username" password = "password"}Copy the code
The deployment diagram is shown below:
Not only that, but you can combine these four best practices based on your actual production situation
4. API support
Note the use of low-level apis here
1. Propagation of the transaction context for remote invocation
Get current XID before remote call:
String xid = RootContext.getXID();
Copy the code
The remote invocation procedure also passes the XID to the service provider, binding the XID to the current application runtime before executing the service provider’s business logic:
RootContext.bind(rpcXid);
Copy the code
2. Transaction suspension and resumption
In a global transaction, if you need some business logic outside the scope of the global transaction, unbind the XID before calling:
String unbindXid = RootContext.unbind();
Copy the code
XID = XID; XID = XID; XID = XID;
RootContext.bind(unbindXid);
Copy the code
Five, possible problems
Undo_log log_status=1
- Scenario: After branch transaction A registers a TC, a global transaction rollback occurs before the local transaction of A is committed
- Consequence: The global transaction is rolled back successfully, and resource A is occupied, causing resource suspension
- Anti-suspension measures: If the undo of the rollback is not inserted when user A rolls back, an undo record with log_status=1 is inserted. When user A commits a local transaction (the SQL service write operation and the corresponding Undo are a local transaction), the transaction fails due to a unique index conflict of the undo table.
2. How to ensure the isolation of things?
Because seATA phase 1 local transactions have been committed, isolation is required to prevent other transactions from dirty reads and writes.
- Dirty read SELECT statements add for UPDATE, proxy methods add @GlobalLock+@Transactional or @GlobalTransaction
- Dirty writes must use @globalTransaction note: If the business interface you are querying does not have a GlobalTransactional wrapper, that is, there is no requirement for distributed transactions on the method at all, you can annotate the @GlobalLock+@Transactional annotation on the method and add for Update to the query. If the interface you are querying has a GlobalTransactional annotation around the transactional link, you can simply query for update. This annotation is designed because without this annotation, a distributed transaction needs to be queried to read committed data, but the business itself does not need a distributed transaction. Using the GlobalTransactional annotation adds unnecessary RPC overhead such as begin returning an XID, committing a transaction, etc. GlobalLock simplifies the RPC process for higher performance.
3. What can I do if dirty data fails to roll?
- You need to manually process dirty data. Modify data as prompted or delete the corresponding undo (you can customize FailureHandler for email notification or other functions).
- Disable undo mirror verification during rollback. This scheme is not recommended.
Note: It is recommended to isolate in advance to ensure no dirty data
4. What should be noted when using AT mode?
- Proxy data sources must be used, and there are three forms to proxy data sources:
- When relying on seta-spring-boot-starter, the data source is automatically proxy without additional processing.
- Rely on seata -all, use the @ EnableAutoDataSourceProxy (since 1.1.0) annotations, annotation parameters can choose the JDK or additional agents.
- You can also use DatasourceProxy to wrap a DataSource manually if you rely on Seata-all.
- GlobalTransactionScanner needs to be configured manually when seata-all is used, but no additional operations are required when Seata-spring-boot-starter is used.
- A service table must contain single-column primary keys. If multiple primary keys exist, see Problem 13.
- Each service library must contain the undo_log table. If it is used with the database and table component, the database and table are not separated.
- Transactions across microservice links require support for the corresponding RPC framework, which is currently supported in SEata-ALL: Apache Dubbo, Alibaba Dubbo, SOFA -RPC, Motan, gRpc, httpClient, for Spring Cloud support, please refer to spring-Cloud-Alibaba-seata. Other self-developed frameworks, asynchronous models and message consumption transaction models should be supported by API.
- AT supports MySQL, Oracle, PostgreSQL, and TiDB.
- When a distributed transaction is enabled with annotations, if the default service provider joins the consumer transaction, the provider does not annotate the transaction. However, providers also require corresponding dependencies and configurations, and only annotations can be omitted.
- When a distributed transaction is started using annotations, if the transaction is required to be rolled back, an exception must be thrown to the originator of the transaction, which is aware of it by the @GlobalTransactional annotation. Provide directly throw an exception or define an error code for the consumer to judge and then throw an exception.
What do I need to notice when using the AT pattern with the Spring @Transactional annotation?
A laparoscope, usually connected to a @ Transactional with DataSourceTransactionManager and JTATransactionManager respectively local transactions and XA distributed transactions, we commonly used is combined with local affairs. When combined with local transactions, @Transactional and @GlobalTransaction are used together, @Transactional can only be at the same method level as the annotation in @GlobalTransaction or within the annotation method in @GlobalTransaction. The concept of a distributed transaction is more important than that of a local transaction. The @Transactional annotation on the outer layer will result in a distributed transaction being committed empty. When a connection corresponding to @Transactional is committed, the global transaction will be reported as being committed or its XID does not exist.
6. SpringCloud XID cannot be delivered?
1. Make sure you introduce spring-cloud-Alibaba-seata dependencies first.
2. If xid also cannot pass, please confirm whether you realized WebMvcConfigurer, if so, please refer to the com. Alibaba. Cloud. Seata. Web. SeataHandlerInterceptorConfiguration# addInterc Eptors’ approach. Add the SeataHandlerInterceptor to your interceptor link.
7. Undolog cannot be deleted after using mybatis- Plus dynamic data source component.
After SEATA is enabled in the dynamic-datasource-spring-boot-starter component, DataSourceProxy is automatically used to package the datasource. Therefore, you need to use the following methods to maintain the compatibility
1. If you introduce is seata – all, please do not use @ EnableAutoDataSourceProxy annotation.
2. If you are introducing seata-spring-boot-starter, disable automatic proxy
seata:
enable-auto-data-source-proxy: false
Copy the code
8. Dirty data cannot be rolled back because automatic timestamp update is enabled in the database?
After SEATA records the current mirror due to service submission, the database updates the timestamp again, causing the mirror verification to fail.
** Solution 1: ** Turn off automatic timestamp updates for the database. Data timestamp updates, such as modification and creation times are maintained at the code level, such as MybatisPlus can be filled automatically.
Solution 2: Update statements do not place unupdated fields in update statements.
9. What are the restrictions on Seata using addresses registered in the registry?
Seata registries cannot register addresses 0.0.0.0 or 127.0.0.1, which can be specified by the startup parameter -h or the container environment variable SEATA_IP when automatically registered. If the registry is on a different network from the service, the registration address can be NAT_IP or a public IP address. Ensure that the health check of the registry is smooth.
10. Seata service self-inspection
1. Check whether the Client. Tm. degradeCheck is true to enable the self-check thread. It can obtain a threshold and a self-check period for survival. It is provided that the survival rate is 10 and 2000. Begin and COMMIT tests are performed every two seconds. If the detection fails, the number of continuous failures is recorded, and the number of continuous failures is emptied. Continuous errors are accumulated by the user interface and the self-check thread until the number of consecutive failures reaches the user threshold. Then, the Seata distributed transaction is closed to prevent the user’s own services from being unavailable for a long time. On the other hand, if the current distributed transaction is closed, the self-check thread continues to self-check every 2 seconds until the number of consecutive successes reaches the user-set threshold, and the Seata distributed transaction is resumed
Set mysql connection parameter rewriteBatchedStatements=true
If the value of rewritebatchedchedstatements is true, execute executeBatch. If the value of rewritebatchedchedstatements is true, execute executeBatch. When the operation type is INSERT, the JDBC driver optimizes the SQL into insert into () values (), () to improve the performance of batch inserts. According to actual tests, the batch insert performance is more than 10 times higher after this parameter is set to true. Therefore, you are advised to set this parameter to true when the data source is MySQL.
Vi. Reference materials
1. Seata FAQ Some common questions to solve
2. Set seATA parameters
3. Introduction to SEATA transaction grouping
Example of SEATA transaction grouping
5. SQL restrictions that can be supported
6. Seata Deployment Guide