适合谁看:要给代购/集运 SaaS 选型存数据方案的后端与 DBA。
不适合可以跳过:单库单商户、没有租户概念的小项目。
上一篇讲状态机时,状态落在哪张表,已经决定了你后三年的迁移成本。这篇把存的问题摊开:八十余个独立站点,数据怎么放,才既隔离又扛得住查询。
仍以 taocarts 这类跨境代购与集运系统的建模思路为例,表名用业务语义称呼,不暴露真实库表名。
1. 痛点:隔离性 vs 成本 vs 变更速度
多租户存储常见三条路:
| 方案 | 隔离 | 成本 | 变更 | 一句话 |
|---|---|---|---|---|
| 共享库共享表 + tenant_id | 靠纪律 | 最低 | 一次迁移全网 | 代码漏 where 就串站 |
| 共享实例多 Schema | 中 | 中 | 中等 | 权限与备份粒度变好一点 |
| 一租户一库 | 强 | 连接/备份变多 | 变更可灰度单站 | 故障面收敛 |
代购站支付、物流、运费差异大,出过错的代价是「串单/串用户」。这种业务里,一租户一库通常比「先共享表以后再说」更便宜——便宜在风险,不在云账单。
2. 中央注册库与业务库分层
不要把「站点列表」和「订单明细」塞同一个业务库硬扛。更干净的分层:
[中央注册库]
- 站点档案:域名、状态、套餐
- 连接信息:业务库 DSN(加密存储)
- 全局开关:维护窗口等
[业务库 × N]
- 用户/订单/包裹/轨迹/财务流水
- 仅本站数据
请求经域名解析拿到站点后,只打开对应业务库连接。报表汇总走离线任务,禁止业务 API「for 每个库 select 一遍」当常态。
3. 订单主数据:单、子单、包裹
一次代购很少是「一行订单搞定」。更常见的拆法:
订单头(Order)
├─ 费用与支付意图(应付、已付、币种、锁价快照)
├─ 子单/采购行(Order Line)—— 每个商品链接/SKU 一行
└─ 包裹(Package)—— 仓内合包后的出库单位
└─ 物流运单(Shipment)—— 国际段单号与渠道
设计要点:
- 行与头分离:采购失败可以落在某一行,不必整单死亡。
- 包裹是履约单位:合包后轨迹挂在包裹/运单上,不要只挂在商品行上导致一对多混乱。
- 状态字段:头状态与行状态、包裹状态分开存;用状态机约束,而不是三个字段随便改。
示意(逻辑模型,非建表语句照抄):
ORDER(id, user_id, state, currency, locked_fx_rate, paid_amount, ...)
ORDER_ITEM(id, order_id, source_url, sku, qty, purchase_state, ...)
PACKAGE(id, user_id, state, weight, ...)
PACKAGE_ITEM(package_id, order_item_id, ...)
SHIPMENT(id, package_id, carrier, tracking_no, ...)
4. 索引:先保高频查询
代购后台最烫的查询通常是:
- 按订单号
- 按用户
- 按国内快递单号(入仓扫描)
- 按国际运单号(客服查件)
- 按状态 + 时间(工作台队列)
原则:
- 等值查询优先独立索引或最左前缀命中。
- 状态队列用
(state, updated_at)类组合,避免state低基数列单打。 - 运单号全局唯一约束要落到「站点库内唯一」;跨站本来就分库,不必强行全球一张唯一表。
- 警惕「所有筛选条件都建联合索引」——写放大比读慢更伤。
慢查询来了先看执行计划,再谈分库分表。很多站的量级,索引与冷热归档就够。
5. 汇率与锁价:钱相关表单独建模
跨境下单最容易扯皮的是:下单汇率和退款汇率不是同一个瞬间。
建议至少:
FX_RATE(currency_pair, rate, source, effective_at) -- 行情/拉取
FX_LOCK(order_id, currency_pair, locked_rate, locked_at) -- 订单锁价快照
下单时把锁价快照写进订单头(或锁价表),退款策略预先定为:按锁价、按退款日行情、或按规则取较差方——策略写进配置,数字写进快照。禁止每次展示现算现改历史订单金额。
金额字段用整数最小币种单位或精确小数类型,禁止 float。
6. 扩展路线:什么时候读写分离,什么时候分表
经验顺序:
- 先:索引、归档历史单、缓存热点配置
- 再:只读副本扛报表与模糊搜索
- 然后:单站内大表按时间分表(订单历史)
- 最后:才考虑更复杂的分布式中间件
一租户一库已经在租户维度分片。多数代购站,瓶颈在「某几个大站的某几张热表」,而不是「八十个小站的总和」。优化要打在热站,而不是平均主义上中间件。
7. 迁移与 DDL
独立库的红利:可以灰度——先迁 1 个站验证 DDL,再滚动。
独立库的代价:DDL 要平台化(任务队列遍历站点连接),禁止人工「开 80 次 navicat」。
变更纪律:
- 兼容性发布:先加列可空 → 再双写 → 再切读 → 再删旧列
- 禁止高峰直接改大表结构无 online ddl 评估
8. 总结
多租户数据库设计,先选隔离方案,再谈性能。代购独立站场景里,一租户一库 + 中央注册 + 订单/行/包裹分层 + 锁价快照,是一套可长期活的底子。分库分表是后手,不是第一天的勋章。
本系列《跨境代购系统技术实战》① 多租户隔离 → ② 订单状态机 → ③ 多租户 MySQL(本文)。后续预告:插件化支付/物流、集运合包算法。
标签:多租户 MySQL 订单建模 索引优化 汇率锁价 代购系统