Understanding Seata: Principles, Deployment, and Real‑World Issues in Distributed Transactions

This article introduces Seata's background, explains the causes of data inconsistency in micro‑service architectures, details Seata's three core roles, transaction flow, isolation levels, common pitfalls with code examples, and provides guidance on high‑availability deployment, multi‑node setups, disaster recovery, and hands‑on demos.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding Seata: Principles, Deployment, and Real‑World Issues in Distributed Transactions

Background

Seata is a distributed transaction middleware adopted by Yixian e‑commerce to solve data inconsistency problems caused by rapid growth of services and complex call chains.

Problem Background

In micro‑service environments, data inconsistency arises from business exceptions, network failures, and service unavailability.

Business Introduction

The article uses simple Yixian e‑commerce scenarios to illustrate the problem.

Principle Analysis

Seata consists of three roles:

Transaction Manager (TM) – client side

Resource Manager (RM) – client side

Transaction Coordinator (TC) – server side

It proxies SQL execution to capture before‑image, after‑image, and undo log for each statement, ensuring atomicity of business and undo‑log operations.

Check that the data source is successfully proxied if a transaction appears ineffective.

First Phase (Prepare)

When an interface annotated with @GlobalTransactional is called, TM intercepts the call, TC creates a global transaction (XID) and returns it. The XID is propagated through the call chain until the end of execution.

Ensure XID is correctly passed down; otherwise the transaction may not work.

Second Phase Commit

If no exception occurs, TC cleans global transaction data and RM removes the corresponding undo log.

Second Phase Rollback

On exception, Seata triggers reverse rollback (C → B → A). If the undo log is missing (e.g., branch registration succeeded but response lost), RM persists a special undo log with status GlobalFinished to prevent inconsistency.

Isolation Level – Read Committed

Seata defaults to READ_UNCOMMITTED. To upgrade to READ_COMMITTED, use @GlobalLock and add FOR UPDATE to the query.

@GlobalLock
@Transactional
public PayMoneyDto detail(ProcessOnEventRequestDto dto) {
    return baseMapper.detail(dto.getProcessInfoDto().getBusinessKey());
}

@Mapper
public interface PayMoneyMapper extends BaseMapper<PayMoney> {
    @Select("select id, name, amount, account, has_repayment, pay_amount from pay_money m where m.business_key = #{businessKey} for update")
    PayMoneyDto detail(@Param("businessKey") String businessKey);
}

Common Issues

TableMeta cache problem : Seata caches table metadata; if the schema changes without restarting the application, a NullPointerException may occur.

@Override
public TableMeta getTableMeta(final Connection connection, final String tableName, String resourceId) {
    if (StringUtils.isNullOrEmpty(tableName)) {
        throw new IllegalArgumentException("TableMeta cannot be fetched without tableName");
    }
    final String key = getCacheKey(connection, tableName, resourceId);
    TableMeta tmeta = TABLE_META_CACHE.get(key, mappingFunction -> {
        try {
            return fetchSchema(connection, tableName);
        } catch (SQLException e) {
            LOGGER.error("get table meta of the table `{}` error: {}", tableName, e.getMessage(), e);
            return null;
        }
    });
    if (tmeta == null) {
        throw new ShouldNeverHappenException(String.format("[xid:%s]get table meta failed, please check whether the table `%s` exists.", RootContext.getXID(), tableName));
    }
    return tmeta;
}

Solution: restart the application after schema changes.

Branch and lock table residue : In MySQL master‑slave setups, data may not sync to the slave, causing missing branch records and global lock acquisition failures.

@Override
public GlobalStatus commit(String xid) throws TransactionException {
    GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
    if (globalSession == null) {
        return GlobalStatus.Finished;
    }
    globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
    boolean shouldCommit = SessionHolder.lockAndExecute(globalSession, () -> {
        globalSession.closeAndClean();
        if (globalSession.getStatus() == GlobalStatus.Begin) {
            if (globalSession.canBeCommittedAsync()) {
                globalSession.asyncCommit();
                return false;
            } else {
                globalSession.changeStatus(GlobalStatus.Committing);
                return true;
            }
        }
        return false;
    });
    if (shouldCommit) {
        boolean success = doGlobalCommit(globalSession, false);
        if (success && globalSession.hasBranch() && globalSession.canBeCommittedAsync()) {
            globalSession.asyncCommit();
            return GlobalStatus.Committed;
        } else {
            return globalSession.getStatus();
        }
    } else {
        return globalSession.getStatus() == GlobalStatus.AsyncCommitting ? GlobalStatus.Committed : globalSession.getStatus();
    }
}

Similar logic applies to rollback.

High‑Availability Deployment

Deploy multiple TC instances with the same registry and configuration center (e.g., Nacos, Consul, etcd3, Zookeeper). Use store.mode=db for persistence.

Single‑Node Multi‑Application Deployment

Use vgroup‑mapping to isolate groups of applications on the same node.

Cross‑Region Disaster Recovery

Configure separate registries for different data centers (e.g., Guangzhou and Shanghai) and switch groups instantly when a region fails.

# Guangzhou data center
registry {
  type = "nacos"
  loadBalance = "RandomLoadBalance"
  loadBalanceVirtualNodes = 10
  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    cluster = "Guangzhou"
  }
}

# Shanghai data center
registry {
  type = "nacos"
  loadBalance = "RandomLoadBalance"
  loadBalanceVirtualNodes = 10
  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    cluster = "Shanghai"
  }
}

Demo

Online hands‑on experience is available at https://start.aliyun.com and a specific Seata demo at https://start.aliyun.com/handson/isnEO76f/distributedtransaction .

Recommended Reading

Links to articles on Kafka architecture, design methodology, and database‑cache consistency are provided for further study.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMicroservicesdatabasehigh availabilityDistributed TransactionsSeata
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.