以订单系统为例,一笔付款会扣账户余额、写一条流水、更新订单状态;月末报表则可能按日期统计所有租户的订单。前者要求几项变更一起提交,后者关心查询会覆盖多大范围。讨论集中式与分布式,先把读写请求分开,比先数数据库节点更容易看清问题。
KingbaseES 核心数据库是集中式关系数据库。KES RAC 和 KES TDC 也都可以部署为多节点形态,但它们的数据组织方式并不相同。
KES、RAC、TDC 的数据组织不是一回事
KES 核心数据库由一个逻辑数据库统一管理数据和事务。KES RAC 采用共享存储架构,多个节点可以并发访问同一套数据,是对等多写集群;增加计算节点不等于把数据切成不同分片。KES RAC 产品说明
KES TDC 是基于 KES 的分布式集群组件,通过扩展计算和存储资源来扩展吞吐量与数据容量。KES TDC 产品说明 它和 RAC 解决的不是同一个部署问题:前者要看数据如何分布、请求如何访问;后者要看共享存储和多写节点如何满足并发与可用性目标。
节点可以横向增加,业务请求却不一定均匀落到各节点;分布键和请求参数要放在一起验证。
评审时可以先把需求说具体:如果应用仍围绕一份逻辑数据运行,主要目标是提升可用性或增加共享数据上的并发处理能力,RAC 可以纳入候选;如果容量或吞吐需要横向扩展,且业务请求能利用稳定的数据键定位,才有理由进一步验证 TDC。集群节点数本身不能替代这两项判断。
“集中式”指逻辑数据和事务由一个数据库统一管理,不等于只能运行在一台服务器。需要故障切换时看可用性方案;要并发访问同一份数据时看共享存储和节点能力;需要扩展数据与吞吐时再看分布方式。
RAC 的计算节点共用存储,因而评估时不能只压数据库节点,也要看共享存储在并发写入和报表读取同时出现时的延迟与吞吐。若存储链路先到上限,计算节点再增加也不会改变这条链路的能力。TDC 则要分别核算计算与存储需求,并确认数据分布后常用请求能否找到目标范围。这些条件决定了 POC 应该观察什么。
先把付款事务写清楚
一次付款涉及余额、流水和订单状态。应用应在同一个事务中按顺序执行下面的 DML;事务开始和提交由应用的事务管理器控制,: 开头的是绑定参数。扣款和订单状态更新后都要检查影响行数,结果不是 1 行就回滚,不能继续提交。
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';
余额更新条件把“余额足够”放进了写入语句;订单状态条件则挡住了对非待支付订单的重复转换。应用还要先校验金额大于 0,并通过业务请求号或流水唯一约束处理重复请求。若更新订单时发现状态已变化,整个事务回滚,前面的余额扣减和流水也不能留下。
还要考虑一种常见的重试情况:数据库已经提交,应用却在收到响应前超时。客户端再发一次请求时,服务端应先用付款请求号判断这笔业务是否已经完成,而不是重新扣款。唯一约束负责挡住重复流水,接口幂等逻辑负责把已有结果返回给调用方;这两件事要一起设计。
在集中式数据库里,这三张表可以由一个逻辑数据库事务统一提交。进入 TDC 方案后,还要按目标版本和表分布方式验证这些表如何定位、事务覆盖范围是什么、失败时应用收到什么结果。付款的成功、余额不足、重复提交和连接中断都要纳入回归;只跑通三条 DML 还不能证明整条支付路径正确。
常用查询带不带租户键
订单详情通常能按租户和时间范围查找:
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;
财务月报的访问方式不同,条件里没有租户键:
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;
假设分布设计使用 tenant_id 作为数据键,第一类请求可以利用这个键缩小访问范围;第二类请求要读取各租户在时间窗口内的数据,再按状态汇总。实际访问多少节点,取决于表的分布定义、查询计划和目标版本,不能只看 SQL 里有没有 GROUP BY。
分布键应从请求条件和表关系里找:付款涉及的余额、流水、订单能否按相同租户键放在一起,租户详情接口是否始终带这个条件,全租户报表会访问哪些数据范围,都要对照实际 SQL 和表结构确认。若为了把一个大租户的写流量拆开而再引入账户键,付款相关表就可能不再处于同一数据范围,跨范围事务和全局报表的代价也要重新评估。
请求路径看完,再看数据是否倾斜。下面的统计列出指定时间窗内订单量最多的租户:
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;
订单量不是请求量。这个结果要和应用侧的租户请求次数、并发数及响应时间放在一起看:订单很多的租户未必最忙,订单不多的租户也可能集中发起写入。若把同一租户的账户、流水和订单按共同键组织,付款请求有机会落在较窄的数据范围;但某个账户上的高并发写入仍然是热点,换成分布式架构不会自动消除它。
再把查询结果和应用监控里的请求分布对齐。若某个租户既占大量存储,又持续占较高写入并发,单一租户键可能形成明显倾斜;如果订单详情都按租户查,但清算任务按全库时间窗口扫,两个请求族对分布键的要求也不一样。分布方案要先照顾主流路径,再为全局查询估算额外读取和汇总成本,不能只按“数据平均分开”来预期收益。
租户键的字段类型、编码规则和空值约束也要统一,否则请求虽带了键,路由条件仍可能与表定义不匹配。
付款接口和月报还应放在同一轮混合负载里跑。月报即使调用次数不多,也可能长时间读取大量数据,挤占存储带宽或临时空间;只分别压测支付和报表,看不出两者同时运行时的影响。按月报实际时间窗和数据保留量运行,再比较付款接口的 P95/P99 延迟及资源等待。报表允许延迟多久,也要在架构评审时写成业务条件。
订单详情里的等值租户条件和时间范围,可以作为复合索引的候选条件。在集中式基线上,可先检查类似下面的索引能否改善计划,再用写入压测确认代价:
CREATE INDEX idx_biz_order_tenant_time_id
ON biz_order (tenant_id, created_at, order_id);
索引会增加存储占用,也会增加插入和更新的维护开销;如果收益只出现在脱离真实数据分布的测试表上,就不该直接带进生产。进入 TDC 的 POC 后,索引的建立方式和实际计划也应在目标版本里重新确认。
先分清资源瓶颈
如果扩容讨论是由 CPU 告警触发,先把最常执行的 SQL 和等待类型列出来。执行计划显示大量无效扫描时,先确认过滤条件和索引;锁等待集中在同一账户时,增加计算节点并不会让这个账户同时由多个节点安全扣款;报表长期读取全租户数据,则要单独衡量它对在线付款的影响。三种现象的处理路径不同,不能统称为“数据库扛不住”。
例如,单租户订单查询的计划可以这样查看:
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 用来检查优化器准备怎样执行这条 SQL;它不是吞吐测试,也不替代实际并发下的耗时与等待统计。先看计划有没有把租户和时间条件用上,再用代表性数据测索引和资源配置,结果比仅凭总行数决定是否分片更有参考价值。
故障恢复和资源扩展也要演练
节点重新显示为运行状态,不等于应用已经恢复。测试时要记录从故障发生到应用成功重连的时间,并分别检查新请求、执行中的付款和已提交事务。客户端收到连接中断时,可能不知道刚才的付款是否已经提交;按请求号查询结果并安全重试,必须和故障演练放在一起验证。
若评估 RAC,故障场景要覆盖节点退出后其他节点继续服务、客户端连接重建和共享存储状态;若评估 TDC,除节点异常外,还要验证增加资源后的数据分布变化和在线请求表现。备份恢复也要使用接近目标容量的数据量测试。平时能处理的 QPS 不代表维护或恢复窗口一定够用,容量方案要给这些操作留出时间和资源。
用同一批请求做验证
比较 KES、RAC 和 TDC 时,测试数据、SQL、并发曲线和客户端连接方式尽量保持一致。租户数据要保留真实的长尾和大租户比例,否则平均分布会掩盖热点。请求集里既要有三表付款和单租户时间查询,也要有全租户汇总、热点账户并发写入,以及节点异常后的连接恢复。
每轮还要记下表定义、索引和数据统计信息。若同时改了 SQL、索引和并发参数,测试结果就难以归因;一次调整一项,再重跑同一请求集,才能看出变化来自哪里。
每轮测试记录成功率、P95/P99 延迟、错误、CPU 与 I/O 使用、锁等待和连接恢复时间。付款完成后核对订单、余额和流水;重复发送同一付款请求,再确认业务结果没有重复。若 TDC 的评估目标是横向扩展,还要在扩展资源前后重跑相同请求,观察数据调整期间的在线请求和批任务表现。性能数字只有在这些条件都相同时才值得横向比较。
如果订单业务仍依赖同一逻辑数据库里的多表事务,集中式 KES 是直接的基线;需要共享存储下的多写访问,可以验证 RAC;需要计算和存储横向扩展,再用代表性负载验证 TDC 的数据定位、跨范围汇总和事务结果。评审表里先写清楚三件事:付款涉及的表如何共置,常用查询是否带租户键,全租户报表需要读取多大范围。它们能直接变成 POC 的输入和验收项。