Distributed transaction selection: XA, 2PC, TCC, Saga, Ali Seata

In the past few years since the rise of microservices, many distributed transaction frameworks have emerged, such as ByteTCC, TCC-transaction, EasyTransaction, and the recently popular Seata. Recently, I just looked at Seata's source code (v0.5.2) and took the opportunity to record some of my understanding of distributed transactions. (3 years ago, this kind of framework was not yet mature, because the project needed to write a flexible transaction framework by itself).

This article is divided into five parts. First, the evolution of the concept of distributed transactions is clarified, and then briefly why you do n’t need XA. The third part explains the "lift" of the two-phase submission. The fourth part introduces the highlights and problems of Seata ’s architecture. Talk about the choice of distributed transactions.

Due to space limitations, some online searchable details will not be elaborated in this article (for example, detailed introduction of principles such as XA, Saga, TCC, Seata, etc.).

First, the generalization of distributed transactions

When it comes to distributed transactions, the earliest refers to database transaction issues involving multiple resources.

Wiki's definition of distributed transactions: A distributed transaction is a database transaction in which two or more network hosts are involved.

However, the meaning of the term transaction gradually expands with the SOA architecture, and can be divided into two categories according to the context:

  • System transaction;

  • Business transaction。

The former refers to database transactions, while the latter corresponds to a business transaction.

At the same time, the meaning of distributed transactions is also generalizing, especially after the concept of SOA and microservices became popular, it mostly refers to a business scenario. When you need to arrange many independently deployed services, how to ensure the atomicity and consistency of the overall transaction Sexual issues. This type of distributed transaction is also called a long-lived transaction, such as a fixed-travel transaction, which consists of buying a flight, renting a car, and booking a hotel. Flight reservations may take a day or two to be confirmed. In order to unify the understanding of concepts, this article refers to such long transactions by default.

The generalization of the concept of distributed transactions also brings a technical problem. How should the ACID of such distributed transactions under microservices be guaranteed? Can it still be solved with traditional two-phase commit / XA? It's a pity that the database-based XA is a bit like a do-it-yourself Adou and it doesn't work.

2. Why doesn't everyone use XA?

In fact, it is not necessary. For example, many cross-resources based on CICS on IBM mainframes are distributed transactions based on the XA protocol. In fact, XA is also a standard for distributed transaction processing, but it is rarely used in the Internet. I think there are the following:

  • Performance (blocking protocol, increase response time, lock time, deadlock);

  • Database support perfection (all have defects before MySQL 5.7);

  • The coordinator relies on independent J2EE middleware (early heavyweight Weblogic, Jboss, late lightweight Atomikos, Narayana and Bitronix);

  • O & M is complex, and DBA lacks experience in this area;

  • Not all resources support the XA protocol;

  • Big factories do n’t understand it, so small companies do n’t dare to use it.

Strictly speaking, XA is a specification and protocol. It only defines a series of interfaces. However, most of the current implementations of XA are databases or MQ. Therefore, XA often refers to the underlying distributed transaction solution based on the resource layer. In fact, some data sharding frameworks or middleware also support the XA protocol. After all, its compatibility and universality are better.

3. "Promotion" submitted in two stages

The database-based XA protocol is essentially a two-phase commit, but it is not suitable for high concurrency scenarios on the Internet due to performance reasons. If the database can only guarantee the local ACID, how to achieve the atomicity of the entire transaction after the transaction abnormality occurs, so as to ensure the consistency C? In addition, how to ensure isolation during processing?

The most direct method is to call the services one by one in accordance with the logic, but what happens if there is an exception? Then compensate for those that have been successfully compensated, and the compensation is successful. The simple model is Saga. But Saga did not guarantee isolation in this way, so TCC appeared. Before the actual transaction logic, check the business and perform "reservation" on the related business resources, or a "intermediate state". If all reservations are successful, complete the real business processing of these reserved resources, typical Scenes such as ticket seats.

Of course, there is also a message table based on Ebay, that is, a consistent model of reliable messages, but essentially this is also a specific implementation of the Saga model. There are two key points:

  • Record execution trajectory based on application shared transactions;

  • Then make sure that the transaction is eventually consistent through asynchronous retry (this also makes this method unsuitable for those scenarios where compensation rollback is allowed in the business).

This type of distributed transaction scenario does not only appear in microservices. In fact, it has existed in the SOA era. Common Saga, TCC, and reliable message finally consistent models have also existed many years ago, but only in recent years. With the rise of microservices, these solutions have once again attracted attention.

"Saga" reference link: https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf

Carefully compare these solutions with XA, you will find that these solutions are essentially two-phase commit from the resource layer to the application layer.

  • The core of Saga is compensation. The first stage is the normal sequence of service calls (database transactions are submitted normally). If the execution is successful, the second stage will do nothing; but if there is an exception in the execution, the compensation service will be called in sequence. (Generally call the reverse transaction of unexecuted services in reverse order) to ensure the consistency of the entire transaction. Application implementation costs are average.

  • The characteristics of TCC are business resource checking and locking. The first stage checks and the resources are locked. If the first stage is successful, the second stage performs transaction logic on the locked resources. Otherwise, the locked resources are released. Application implementation costs are higher.

  • Based on reliable messages, the services are normally called in the first stage, and the message table is recorded with the transaction. In the second stage, the messages are delivered and consumed. Application implementation costs are low.

Specific to the distributed transaction framework based on these models, it also draws on the DTP (Distributed Transaction Processing) model.

DTP (Distributed Transaction Processing) reference link: http://pubs.opengroup.org/onlinepubs/009680699/toc.pdf

▲ DTP model

  • RM is responsible for the submission of local transactions, and at the same time completes the registration of branch transactions and the determination of locks, playing the role of transaction participant.

  • TM is responsible for triggering the submission and rollback of the overall transaction, and plays the role of overall coordinator of the transaction.

When different frameworks are implemented, the function and deployment form of each component role will be adjusted according to needs. For example, some TMs are deployed together with applications in the form of jar packages, and some are stripped out and need to be deployed separately (for example, the main functions of TM in Seata Put on a logically centralized Server, called TC (Transaction Coordinator))

Fourth, the gains and losses of the Seata architecture

At the beginning of this year, Ali released the open source distributed transaction framework Fescar, which was later renamed to Seata after being integrated with the Ant TCC program. Although the current version only reaches 0.6, the GitHub star has passed 9k. The aspect also shows everyone's expectations for Ali's distributed transaction framework.

Seata's usage and principles have been clearly explained on its github wiki, and there are many articles on the source code analysis on the Internet. Next, we analyze the principle of Seata AT mode to see its highlights and problems.

"Seata usage and principles" reference link: https://github.com/seata/seata/wiki

Seata's support for MT and TCC is limited, and these two modes are more for compatibility with existing application ecosystems.

The Seata team drew a detailed call flow chart, and reading its source code against this figure would be a lot easier.

▲ Seata execution flowchart

1. Highlights

Compared with other distributed transaction frameworks, the main highlights of the Seata architecture are several:

  • The application layer implements automatic compensation based on SQL parsing, thereby minimizing business intrusion;

  • Independent deployment of TC (transaction coordinator) in distributed transactions, responsible for registration and rollback of transactions;

  • Through the global lock to achieve write isolation and read isolation.

The specific implementation mechanisms of these features are detailed on the official website and github, and will not be introduced here.

2. Performance loss

Let's take a look at what overhead Seata has added (negligible for pure memory operations):

An Update SQL requires global transaction xid acquisition (communication with TC), before image (parse SQL, query database once), after image (query database once), insert undo log (write database once), before commit (with TC Communication, to determine the lock conflict), these operations require a remote communication RPC, and are synchronized.

In addition, the insertion performance of the blob field when undo log is written is not high. Each write of SQL will add so much overhead, roughly estimated that it will increase the response time by 5 times (although the second stage is asynchronous, it will actually occupy system resources, network, threads, database).

How to generate front and back images?

Analyze SQL through druid, then reuse the where condition in business SQL, and then generate Select SQL to execute.

3. Cost performance

In order to perform automatic compensation, all transactions need to be mirrored before and after and persisted. However, in actual business scenarios, how high is this success rate, or how many ratios do distributed transaction failures need to be rolled back? This ratio is different in different scenarios. Considering that many transactions will be checked for correctness before performing transaction orchestration, the probability of a rollback is actually relatively low. According to the 28th principle, it is estimated that in order to roll back 20% of the transactions, the response time of 80% of the successful transactions needs to be increased by 5 times. Is this cost more worthwhile than letting the application develop a compensation transaction? It is worthy of our consideration.

The industry also has a way of thinking, through the database binlog to restore the SQL before and after mirroring, which saves the synchronization of undo log generation records, reduces performance loss, and at the same time zero business intrusion, personally feel that it is a better way.

4. Global lock

1) Hotspot data

Seata will carry the corresponding lock information in each branch transaction, and will acquire the locks in sequence before the commit stage (because all SQL information needs to be executed before all lock information is obtained, so it is judged before commit). Compared with XA, Seata will release the database lock after the success of the first stage, but the determination of the global lock before the first stage also lengthens the occupation time of the data lock. This overhead is much lower than XA's prepare according to the actual business scenario carry out testing. The introduction of global locks achieves isolation, but the problem is blocking, reducing concurrency, especially hot data, this problem will be more serious.

2) Rollback lock release time

When Seata rolls back, it is necessary to delete the undo log of each node before releasing the lock in the TC memory, so if the second stage is rollback, the time to release the lock will be longer.

3) Deadlock problem

Seata's introduction of global locks will additionally increase the risk of deadlocks, but if deadlocks are implemented, it will continue to retry and finally wait for the global locks to time out. This method is not elegant, and it also extends the occupation time of database locks.

"The introduction of Seata global lock will increase the risk of deadlock" reference link: https://github.com/seata/awesome-seata/blob/master/wiki/en-us/Fescar-AT.md

5. Other issues

1) For some applications that use Seata, how to ensure that the data is not dirty or phantom?

Seata provides a @GlobalLock annotation, which can provide the function of lightweight global lock determination (do not generate undo log), but it still needs to be integrated to use Seata.

2) TC is logically a single point, and how to achieve high availability and high performance still needs continuous optimization in subsequent versions.

3) Stand-alone multi-data source cross-service is currently not supported.

Five, the choice of distributed transactions

Strict ACID transactions have high requirements for isolation. All resources must be locked during the execution of transactions. For long transactions, the exclusive use of data during the entire transaction period will seriously affect the concurrent performance of the system. Therefore, in high concurrency scenarios, some features of ACID are relaxed to improve performance, which results in BASE flexible transactions. The idea of ​​flexible transactions is to move the mutex operation from the resource level to the business level through business logic. By relaxing the requirements for strong consistency, the system throughput is improved. In addition, an automatic exception recovery mechanism is provided to ensure the eventual consistency of the transaction after an exception occurs.

If the distributed transaction based on XA must strictly guarantee ACID, the actual transaction isolation level is SERLALIZABLE.

It can be seen from the above that flexible transactions require the application layer to participate, so one of the first functions of this type of distributed transaction framework is to minimize the cost of business transformation, and then to improve performance (response time, throughput) as much as possible, preferably to ensure isolation .

A good distributed transaction framework application meets the following characteristics as much as possible:

  • Low cost of business transformation;

  • Low performance loss;

  • Isolation is guaranteed to be complete.

But like CAP, these three characteristics are mutually check and balance, often can only meet two of them, we can draw a triangle constraint:

Saga based on business compensation meets 1.2; TCC meets 2.3; Seata meets 1.3.

Of course, if we want to design a distributed transaction framework ourselves, we need to consider many other features, and make trade-offs after clearing the preferences of the target scenario. These features include but are not limited to the following:

  • Business intrusion (based on annotations, XML, compensation logic);

  • Isolation (write isolation / read isolation / read uncommitted, business isolation / technical isolation);

  • TM / TC deployment form (separate deployment, together with application deployment);

  • Error recovery (automatic recovery, manual recovery);

  • Performance (probability of rollback, price paid, response time, throughput);

  • High availability (registration center, database);

  • Persistence (database, file, multi-copy consistent algorithm);

  • Synchronous / asynchronous (2PC execution mode);

  • Log cleanup (automatic, manual);

  • ......

6. Conclusion

Distributed transactions have always been a difficult problem in the industry. The difficulty lies in the CAP theorem, the eight major false assumptions in distributed systems, the impossible principle of FLP, and the fact that we are used to comparing ACID for stand-alone transactions. Whether it is the XA, Google percolator or Calvin model in the database field, or the Saga, TCC, reliable messaging and other solutions under microservices, they have not solved the distributed transaction problem perfectly. Seeking trade-offs under certain scene preferences.

Reference link for "8 Distributed System Assumptions": http: //% 5Bhttps: //en.wikipedia.org/wiki/Fallacies_of_distributed_computing%5D (https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing)

"FLP Impossible Principle" reference link: html% 5D (https://www.cnblogs.com/firstdream/p/6585923.html)

In fact, due to the uncertainty of the network, many problems under distribution are problems. The best solution is to avoid distributed transactions :)

Finally, back to the topic, has Seata solved the problem of distributed transactions? See what you care about most. If you want the business to be as sensible as possible and the DB operation is simple, it will surprise you; but if you value response time more, DB write operations are more, and the call chain is longer, then it may disappoint. Finally, I hope the Seata open source project gets better and better!

Published 203 original articles · praised 6 · visits 4482

Guess you like

Origin blog.csdn.net/weixin_42073629/article/details/105545392