当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL 多租户订单表架构演进:从 tenant_id 联合索引到租户分片

MySQL 多租户订单表架构演进:从 tenant_id 联合索引到租户分片

来源:17golang原创 2026-07-02 13:34:56 0浏览 收藏

多租户系统里的订单表,早期通常会把所有租户的数据放在一张 orders 表里,再用 tenant_id 区分归属。数据量小的时候这很清爽;一旦某个大客户的订单量、查询量和导出任务明显高于其他租户,单表联合索引只能解决一部分查询成本,真正的架构问题会变成:哪些租户继续共享,哪些租户需要被路由到独立资源里。

核心要点
  • 多租户订单表的第一条规则,是所有核心查询都必须带上 tenant_id,并让联合索引从租户维度开始。
  • 联合索引能减少扫描范围,但不能隔离一个热点租户对 CPU、IO、连接池和慢查询队列的影响。
  • 当大租户长期拉高 rows、慢日志和接口延迟,就要把“加索引”升级为“租户路由 + 数据迁移 + 回读校验”。
  • 分区表可以帮助部分查询裁剪无关分区,但它不是租户级资源隔离方案,主键和唯一键限制也要提前核对。
目录
  • 规模背景:一张 orders 表承载所有租户
  • 原架构瓶颈:一个热点租户拖慢整张订单表
  • 第一阶段:用 tenant_id 领头的联合索引稳住主查询
  • 第二阶段:拆出热点租户的路由和写入链路
  • 关键取舍:分区、分表和独立库分别解决什么问题
  • 上线后看哪些信号
  • 相关问题
  • 总结

规模背景:一张 orders 表承载所有租户

先看一个常见表结构。订单表既要给后台列表查,又要给对账、导出、售后、统计任务用。早期为了开发简单,所有租户共享一张表:

CREATE TABLE orders (
  id BIGINT PRIMARY KEY,
  tenant_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  status TINYINT NOT NULL,
  amount DECIMAL(12, 2) NOT NULL,
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL
);

列表查询一般长这样:

SELECT id, status, amount, created_at
FROM orders
WHERE tenant_id = ?
  AND status = ?
  AND created_at >= ?
ORDER BY created_at DESC
LIMIT 50;

这个模型的优点很明显:表少、代码简单、统计也容易写。问题也同样明显:所有租户共用同一张物理表、同一组索引、同一个实例资源。只要一个租户的数据量明显偏大,或者某个租户开启高频导出,其他租户的正常查询也可能被拖住。

原架构瓶颈:一个热点租户拖慢整张订单表

MySQL 官方文档说明,索引用于快速找到具有特定列值的行;多列索引可以服务测试索引中全部列或最左前缀列的查询。这给了我们第一层优化方向:把高频条件放进合适的联合索引里。但多租户场景的麻烦在于,热点租户并不只是“查询没走索引”,它常常是“走了索引也要读很多行”。

MySQL 多租户 orders 表中 tenant_id 查询命中索引但热点租户 rows high 后需要 split tenant 的决策路径图

假设普通租户每月只有几千单,热点租户每月有几百万单。相同的 SQL、相同的索引,在普通租户上可能只扫几十行,在热点租户上却要扫大量历史记录。此时单表继续扩容会遇到几个瓶颈:

  • 索引页更大,缓存命中率下降,热点租户把更多 buffer pool 空间占走。
  • 大范围查询和导出任务增加磁盘读写压力,影响普通租户列表页。
  • 慢查询排队会占用连接池,让应用侧看起来像“所有租户都慢”。
  • 归档、修复、回放这类后台任务越来越难按租户隔离。

第一阶段:用 tenant_id 领头的联合索引稳住主查询

在没有分片之前,先把主查询的索引设计做好。对上面的订单列表,可以先建一个符合过滤和排序方向的联合索引:

CREATE INDEX idx_orders_tenant_status_created
ON orders (tenant_id, status, created_at DESC);

这样做的核心不是“字段越多越好”,而是把租户边界放在最前面。多列索引有最左前缀规则,tenant_id 作为第一列,可以让同一租户内的状态和时间范围查找更集中。验证时看三类信号:

检查项 希望看到的变化 说明
EXPLAINkey 使用 idx_orders_tenant_status_created 说明优化器选择了目标索引
rows 从大范围下降到租户内较小范围 说明扫描范围被租户和条件收窄
慢日志 普通租户查询明显减少 说明主链路先被稳住

如果列表还需要按用户查,可以再根据真实查询频率补充 (tenant_id, user_id, created_at)。不要给每个接口都加一条索引,索引越多,写入、更新和空间成本也越高。

第二阶段:拆出热点租户的路由和写入链路

当联合索引已经命中,但热点租户仍然长期拉高延迟,就要从表设计进入路由设计。比较稳的做法不是一次性全量拆所有租户,而是先给热点租户建立路由表:

CREATE TABLE tenant_db_route (
  tenant_id BIGINT PRIMARY KEY,
  route_type VARCHAR(20) NOT NULL,
  shard_key VARCHAR(64) NOT NULL,
  updated_at DATETIME NOT NULL
);

应用写入订单前先查本地缓存的租户路由:普通租户继续写共享库,热点租户写独立分片。读接口也走同一套路由,避免写到新分片、读还去旧表的割裂问题。

MySQL 多租户系统通过 route table 把普通租户写入 orders_01、热点租户写入 tenant shard 和 orders_02 的分片流程图

迁移时建议按下面顺序推进:

  1. 先建新分片和目标表,表结构、索引、字符集和时区规则保持一致。
  2. 按租户维度复制历史数据,复制后比对订单数、金额合计和最大 id
  3. 应用侧开启双读校验,只对目标租户生效,发现差异可以退回共享表读取。
  4. 切写入路由,让热点租户的新订单进入新分片。
  5. 观察一段时间后,再清理旧表中已经迁出的热点租户数据。

这一步的关键是“按租户渐进迁移”。如果一开始就分所有租户,很容易把路由、迁移、回滚和报表链路一起复杂化。

关键取舍:分区、分表和独立库分别解决什么问题

多租户订单表变慢时,团队常会在分区、分表、分库之间摇摆。它们解决的问题不同,不能只看名字相似。

方案 主要收益 适合场景 需要注意
联合索引 减少单次查询扫描范围 大多数租户查询还在可控范围内 无法隔离热点租户资源消耗
MySQL 分区 在条件可裁剪时减少无关分区扫描 按时间或固定键管理历史数据 主键、唯一键和分区表达式有限制,不能当成完整分片
租户分表 降低单表数据量,迁移边界清晰 热点租户少、表结构稳定 报表和跨租户查询要额外聚合
独立库或独立实例 隔离连接、IO、缓存和维护窗口 大客户、强隔离、付费等级差异明显 运维成本、路由和备份策略都会变复杂

MySQL 分区裁剪的思路是:当条件能明确落到某些分区时,就不扫描不可能命中的分区。这个能力很适合按时间清理和部分范围查询,但它仍在同一个表模型里工作。官方文档也明确提到分区键与主键、唯一键之间有约束关系,做方案前要先核对现有唯一约束是否允许这样改。

上线后看哪些信号

拆分不是把数据搬走就结束。上线后至少要观察三个层面的信号:

  • 查询层:热点租户迁出后,共享表主查询的 rows、慢日志次数、接口 P95 是否下降。
  • 写入层:热点租户新订单是否全部进入新分片,路由缓存是否有过期和误命中。
  • 运维层:备份、归档、数据修复、账单统计是否已经适配新的路由关系。

更稳的验收方式,是把迁移前后的核心 SQL 都留一份样例:

EXPLAIN
SELECT id, status, amount, created_at
FROM orders
WHERE tenant_id = 8421
  AND status = 2
  AND created_at >= '2026-07-01'
ORDER BY created_at DESC
LIMIT 50;

如果热点租户被迁出,共享表上这类查询应该不再拖累其他租户;新分片上的查询则要单独看索引、归档和限流策略。分片后的性能治理不是结束,而是把“全局混在一起慢”改成“按租户定位和治理”。

相关问题

多租户表一定要用 tenant_id 做联合索引第一列吗?

大多数按租户隔离的业务查询都应该这样做。只要接口天然属于某个租户,tenant_id 放在联合索引前面可以先收窄租户范围,再按状态、时间或用户继续过滤。

热点租户出现后,应该先分表还是先独立库?

先看瓶颈在哪里。如果只是单表过大,分表可能够用;如果连接、缓存、IO 和维护窗口都被大租户占用,独立库或独立实例更符合隔离目标。

MySQL 分区能不能替代租户分片?

通常不能。分区可以帮助管理和裁剪部分查询范围,但它不等于资源隔离,也不能替代应用层路由。多租户隔离通常还要考虑连接池、备份、权限、账单和运维边界。

租户迁移时最怕什么问题?

最怕写入和读取路由不一致。建议先做历史数据校验,再做双读或抽样回读,最后切写入路由,并保留可退回共享表的开关。

总结

MySQL 多租户订单表的演进,不是一上来就分库分表。更可靠的路线是:先保证所有主查询带 tenant_id,用租户维度领头的联合索引压低普通查询成本;当热点租户继续制造高扫描、高延迟和队列压力,再通过租户路由把它迁到独立分片。这样既保留早期单表的简单性,也给大客户和高峰流量留出清晰的扩展路径。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux rsync 同步目录如何排除文件并保留权限?安全命令配方Linux rsync 同步目录如何排除文件并保留权限?安全命令配方
上一篇
Linux rsync 同步目录如何排除文件并保留权限?安全命令配方
Go 服务的 pprof 能直接暴露公网吗?排障入口上线前的安全判断
下一篇
Go 服务的 pprof 能直接暴露公网吗?排障入口上线前的安全判断
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    4700次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4308次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4259次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    4483次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4443次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码