跪拜 Guibai
← Back to the summary

Order Read/Writes Expose Where KingbaseES Centralized and Distributed Architectures Diverge

Two access shapes for order payment and tenant-wide reporting

Using an order system as an example: a payment debits account balance, writes a ledger entry, and updates order status; a month-end report might aggregate all tenants' orders by date. The former requires several changes to commit together, the latter cares about how wide a query range. When discussing centralized vs. distributed, separating read and write requests first makes the issue clearer than counting database nodes first.

KingbaseES core database is a centralized relational database. Both KES RAC and KES TDC can be deployed in multi-node form, but their data organization differs.

KES, RAC, TDC data organization is not the same

The KES core database manages data and transactions uniformly through one logical database. KES RAC uses a shared-storage architecture; multiple nodes can concurrently access the same data set and form a peer multi-write cluster; adding compute nodes does not mean splitting data into different shards. KES RAC product description

KES TDC is a distributed cluster component based on KES, extending throughput and data capacity by expanding compute and storage resources. KES TDC product description It and RAC do not solve the same deployment problem: the former looks at how data is distributed and how requests access it; the latter looks at how shared storage and multi-write nodes meet concurrency and availability goals.

Nodes can be added horizontally, but business requests do not necessarily land evenly on each node; the distribution key and request parameters must be verified together.

Architecture positioning of KingbaseES, KES RAC, and KES TDC

When reviewing, first make the requirements concrete: if the application still runs around a single logical data set and the main goal is improving availability or increasing concurrent processing on shared data, RAC can be a candidate; if capacity or throughput needs horizontal scaling and business requests can be located using a stable data key, then it is justified to further verify TDC. The number of cluster nodes alone cannot replace these two judgments.

"Centralized" means logical data and transactions are managed uniformly by one database; it does not mean it can only run on one server. When failover is needed, look at the availability solution; when concurrent access to the same data is needed, look at shared storage and node capabilities; when data and throughput need to scale, look at the distribution method.

RAC compute nodes share storage, so when evaluating you cannot only stress the database nodes; you must also look at the latency and throughput of shared storage when concurrent writes and report reads occur together. If the storage link reaches its limit first, adding compute nodes will not change that link's capability. TDC, on the other hand, must budget compute and storage needs separately and confirm that common requests can locate their target range after data distribution. These conditions determine what a POC should observe.

Write the payment transaction clearly first

A payment involves balance, ledger, and order status. The application should execute the following DML in order within the same transaction; transaction start and commit are controlled by the application's transaction manager, and parameters starting with : are bind parameters. After the debit and order status update, check affected row counts; if the result is not 1 row, roll back and do not continue committing.

UPDATE account_balance
   SET available_amount = available_amount - :payment_amount
 WHERE tenant_id = :tenant_id
   AND account_id = :account_id
   AND available_amount >= :payment_amount;

INSERT INTO account_ledger
    (ledger_id, tenant_id, account_id, order_id, change_amount, created_at)
VALUES
    (:ledger_id, :tenant_id, :account_id, :order_id,
     -:payment_amount, CURRENT_TIMESTAMP);

UPDATE biz_order
   SET order_status = 'PAID'
 WHERE tenant_id = :tenant_id
   AND order_id = :order_id
   AND order_status = 'WAITING';

The balance update condition places "sufficient balance" into the write statement; the order status condition blocks repeated transitions on non-pending orders. The application should also validate that the amount is greater than 0 and handle duplicate requests via a business request ID or unique constraint on the ledger. If the order status changes during the update, the entire transaction rolls back, leaving no balance debit or ledger entry.

Another common retry scenario must be considered: the database has already committed, but the application times out before receiving a response. When the client sends the request again, the server should first use the payment request ID to determine whether the business is already completed, rather than debiting again. A unique constraint blocks duplicate ledger rows, while interface idempotency logic returns the existing result to the caller; these two must be designed together.

In a centralized database, these three tables can be committed uniformly by one logical database transaction. Under a TDC scheme, you must verify how these tables are located, what the transaction coverage is, and what result the application receives on failure against the target version and table distribution. Payment success, insufficient balance, duplicate submission, and connection interruption must all be included in regression; only running three DML statements does not prove the entire payment path is correct.

Do common queries carry a tenant key?

Order details can usually be looked up by tenant and time range:

SELECT order_id,
       order_status,
       order_amount,
       created_at
FROM biz_order
WHERE tenant_id = :tenant_id
  AND created_at >= :window_start
  AND created_at <  :window_end
ORDER BY created_at, order_id;

Financial monthly reports access data differently; the condition does not include a tenant key:

SELECT order_status,
       COUNT(*) AS order_count,
       SUM(order_amount) AS amount_sum
FROM biz_order
WHERE created_at >= :window_start
  AND created_at <  :window_end
GROUP BY order_status
ORDER BY order_status;

Assuming the distribution design uses tenant_id as the data key, the first type of request can use this key to narrow the access range; the second type must read each tenant's data within the time window and aggregate by status. How many nodes are actually accessed depends on the table's distribution definition, query plan, and target version; you cannot judge only by whether the SQL contains GROUP BY.

Access range sketch for tenant-key queries and tenant-wide aggregation

The distribution key should be derived from request conditions and table relationships: whether balance, ledger, and order involved in payment can be co-located by the same tenant key, whether the tenant detail endpoint always carries this condition, and what data ranges the tenant-wide report accesses, all must be confirmed against actual SQL and table structures. If an account key is introduced to split a large tenant's write traffic, payment-related tables may no longer fall in the same data range; the cost of cross-range transactions and global reports must then be re-evaluated.

After examining the request path, look at data skew. The following statistics list tenants with the most orders in a specified time window:

SELECT tenant_id,
       COUNT(*) AS order_count,
       MIN(created_at) AS first_order_time,
       MAX(created_at) AS last_order_time
FROM biz_order
WHERE created_at >= :window_start
  AND created_at <  :window_end
GROUP BY tenant_id
ORDER BY order_count DESC
LIMIT 20;

Order count is not request count. This result must be examined together with application-side tenant request counts, concurrency, and response times: a tenant with many orders may not be busiest, and a tenant with few orders may concentrate writes. If the same tenant's account, ledger, and orders are organized by a common key, payment requests have a chance to land in a narrow data range; but high-concurrency writes on a single account still form a hot spot, and a distributed architecture will not automatically eliminate it.

Then align query results with request distribution in application monitoring. If a tenant occupies both large storage and sustained high write concurrency, a single tenant key may create obvious skew; if order details are queried by tenant but settlement tasks scan the whole database by time window, the two request families make different demands on the distribution key. A distribution plan should first serve the mainstream path, then estimate the extra read and aggregation cost for global queries, and not expect gains merely from "evenly splitting data."

The data type, encoding rules, and null constraints of the tenant key must also be uniform, otherwise a request carrying the key may still fail to match the routing condition against the table definition.

Payment endpoints and monthly reports should also run in the same mixed-load round. Even if a monthly report is called infrequently, it may read large amounts of data for a long time, consuming storage bandwidth or temporary space; only separately stressing payment and reports does not reveal their interaction when running simultaneously. Run with the actual monthly-report time window and data retention volume, then compare the payment endpoint's P95/P99 latency and resource waits. How much delay a report may tolerate should also be written into the architecture review as a business condition.

The equal-tenant condition and time range in order details can be candidates for a composite index. On the centralized baseline, first check whether an index like the following improves the plan, then use write stress to confirm the cost:

CREATE INDEX idx_biz_order_tenant_time_id
    ON biz_order (tenant_id, created_at, order_id);

An index increases storage usage and adds maintenance overhead on inserts and updates; if the benefit appears only on test tables detached from real data distribution, it should not be taken into production directly. After entering a TDC POC, the index creation method and actual plan should be re-confirmed on the target version.

Distinguish resource bottlenecks first

If scaling discussion is triggered by CPU alerts, list the most frequently executed SQL and wait types first. When an execution plan shows many invalid scans, first confirm filter conditions and indexes; when lock waits concentrate on the same account, adding compute nodes will not let multiple nodes safely debit that account at the same time; when reports read all-tenant data for a long time, measure their impact on online payments separately. The three phenomena require different handling paths and cannot be lumped together as "the database cannot handle it."

For example, the plan for a single-tenant order query can be viewed as:

EXPLAIN
SELECT order_id,
       order_status,
       order_amount,
       created_at
FROM biz_order
WHERE tenant_id = :tenant_id
  AND created_at >= :window_start
  AND created_at <  :window_end
ORDER BY created_at, order_id;

EXPLAIN is used to check how the optimizer plans to execute this SQL; it is not a throughput test and does not replace latency and wait statistics under real concurrency. First look at whether the plan uses the tenant and time conditions, then test index and resource provisioning with representative data; the result is more informative than deciding to shard based only on total row count.

Fault recovery and resource expansion also need drills

A node showing as running again does not mean the application has recovered. During tests, record the time from failure to successful application reconnection, and check new requests, in-flight payments, and committed transactions separately. When a client receives a connection interruption, it may not know whether a previous payment has already committed; querying the result by request ID and safely retrying must be verified together with failure drills.

If evaluating RAC, failure scenarios must cover other nodes continuing service after a node exits, client connection re-establishment, and shared storage state; if evaluating TDC, besides node anomalies, verify the data distribution changes and online request behavior after adding resources. Backup recovery should also be tested with data volumes close to target capacity. QPS that the system can handle in normal times does not guarantee maintenance or recovery windows; capacity planning must reserve time and resources for these operations.

Use the same request set to verify

When comparing KES, RAC, and TDC, keep test data, SQL, concurrency curves, and client connection methods as consistent as possible. Tenant data must preserve real long-tail and large-tenant proportions; otherwise an even distribution masks hot spots. The request set should include three-table payment, single-tenant time queries, tenant-wide aggregation, hot-account concurrent writes, and connection recovery after node failures.

Record table definitions, indexes, and data statistics each round. If SQL, indexes, and concurrency parameters are changed at the same time, results are hard to attribute; adjust one thing at a time and rerun the same request set to see where changes come from.

Each round records success rate, P95/P99 latency, errors, CPU and I/O usage, lock waits, and connection recovery time. After payment completes, check order, balance, and ledger; send the same payment request again and confirm the business result is not duplicated. If the TDC evaluation targets horizontal scaling, rerun the same requests before and after adding resources and observe online requests and batch tasks during data adjustment. Performance numbers are worth comparing only when these conditions are the same.

If the order business still relies on multi-table transactions in the same logical database, centralized KES is the direct baseline; for multi-write access under shared storage, verify RAC; for compute and storage horizontal scaling, use representative load to verify TDC's data location, cross-range aggregation, and transaction results. Write three things clearly in the review table: how the tables involved in payment are co-located, whether common queries carry a tenant key, and what range tenant-wide reports read. These can directly become inputs and acceptance criteria for a POC.