导读:本期聚焦于小伙伴创作的《mysql如何优化多租户系统中的索引结构_针对租户ID的复合索引》,敬请观看详情。把租户ID放在复合索引最左列还是中间列,直接决定了多租户系统里大多数查询能否走索引。不少团队在分库分表前先用逻辑隔离,所有表都带tenant_id字段,但索引建错顺序后,按租户加时间范围查订单依然全表扫描。正确做法是把tenant_id作为复合索引首列,配合高频过滤字段与排序字段构建覆盖索引,避免回表。同时要权衡写入放大与查询命中率,对超大数据量租户考虑分区表或独立schema。本文从执行计划、索引选择性、索引下推等角度说明具体落地方式。

在多租户系统里,所有业务表通常都会带一个tenant_id字段用来做逻辑隔离。当单表数据量上升到千万甚至上亿级别时,如果索引结构不合理,绝大多数以租户为条件的查询都会变成全表扫描,数据库CPU和IO迅速被打满。针对tenant_id设计复合索引,是这类系统最基础也最关键的优化手段。

mysql如何优化多租户系统中的索引结构_针对租户ID的复合索引

为什么tenant_id必须放在复合索引最左列

MySQL的复合索引遵循最左前缀原则。也就是说,一个建立在(tenant_id, created_at, status)上的索引,只有在查询条件里用到了tenant_id,或者用到tenant_id加created_at等左侧连续字段时,才能被有效利用。如果查询只带了created_at而没有tenant_id,这个复合索引就完全派不上用场。

在多租户场景中,几乎每一个查询都会带上tenant_id作为隔离条件。把tenant_id放在最左列,可以保证所有租户相关查询都能命中索引首层,快速缩小数据范围。反之,如果把tenant_id放在第二或第三列,那么只传tenant_id的查询将无法使用该索引,只能退化为全表扫描或依赖其他单列索引,性能差距在大数据量下可达几十倍。

最左前缀的执行计划验证

我们可以通过EXPLAIN来直观对比。假设有如下表和索引:

CREATE TABLE orders (
  id BIGINT PRIMARY KEY,
  tenant_id INT NOT NULL,
  created_at DATETIME NOT NULL,
  status TINYINT NOT NULL,
  amount DECIMAL(10,2),
  INDEX idx_tenant (tenant_id, created_at, status)
) ENGINE=InnoDB;

执行下面两条语句并查看type列:

EXPLAIN SELECT * FROM orders WHERE tenant_id = 10 AND created_at > '2023-01-01';
EXPLAIN SELECT * FROM orders WHERE created_at > '2023-01-01';

第一条会显示range或ref,说明走了idx_tenant;第二条则大概率显示ALL,也就是全表扫描。这清楚地证明了tenant_id必须处于复合索引最左侧。

如何结合高频字段构建覆盖索引

仅仅把tenant_id放最左列还不够。如果查询经常需要过滤status或者按created_at排序,并且select的字段不多,我们可以把这些字段一起放进复合索引,形成覆盖索引,避免回表。

比如运营后台常见查询是:某租户下,某状态订单按时间倒序取前二十条。对应的复合索引应为(tenant_id, status, created_at)。这样MySQL在索引里就能完成过滤和排序,不需要再去主键索引取完整行,大幅降低IO。

覆盖索引示例代码

建表与查询示例如下:

CREATE TABLE orders (
  id BIGINT PRIMARY KEY,
  tenant_id INT NOT NULL,
  status TINYINT NOT NULL,
  created_at DATETIME NOT NULL,
  amount DECIMAL(10,2),
  INDEX idx_cover (tenant_id, status, created_at)
) ENGINE=InnoDB;

SELECT id, amount
FROM orders
WHERE tenant_id = 5 AND status = 1
ORDER BY created_at DESC
LIMIT 20;

上述查询在EXPLAIN的Extra列会显示Using index,表示使用了覆盖索引。如果字段选择不当,比如把amount也频繁查询但没加入索引,就会看到Using where; Using index,仍可能触发随机回表。

索引下推对多租户查询的帮助

MySQL 5.6之后引入了索引下推(Index Condition Pushdown,简称ICP)。在复合索引(tenant_id, status, created_at)中,如果查询条件是tenant_id = ? AND status = ?,但status不在最左前缀的连续使用范围内(比如中间有范围查询打断),ICP允许存储引擎在遍历索引时直接过滤status,而不是把数据读到 server 层再判断。

虽然多租户查询通常tenant_id是等值条件,ICP收益看起来有限,但在复杂组合查询里,比如tenant_id等值、created_at范围、status等值,ICP能减少回表次数。可以通过EXPLAIN的Extra列观察是否出现Using index condition来确认。

查看ICP是否生效

EXPLAIN SELECT * FROM orders
WHERE tenant_id = 3 AND created_at > '2023-06-01' AND status = 2;

若Extra中出现Using index condition,说明ICP已生效,存储引擎层就完成了status过滤,减少了无用回表。

超大租户下的特殊优化思路

有些系统中个别租户的数据量远超其他租户,比如平台自营账号。此时即便有tenant_id复合索引,该租户内部的查询仍要扫描巨量索引条目。可以考虑使用MySQL分区表,按tenant_id做LIST分区,或者将超大租户拆分到独立schema甚至独立实例。

分区表能让查询在确认tenant_id后只访问单个分区,避免跨分区扫描。但要注意分区键必须包含在主键或唯一索引里,否则会报错。下面是按tenant_id分区的简单示例:

CREATE TABLE orders (
  id BIGINT NOT NULL,
  tenant_id INT NOT NULL,
  created_at DATETIME NOT NULL,
  status TINYINT NOT NULL,
  PRIMARY KEY (id, tenant_id),
  INDEX idx_t (tenant_id, created_at)
) ENGINE=InnoDB
PARTITION BY LIST (tenant_id) (
  PARTITION p0 VALUES IN (1,2,3),
  PARTITION p1 VALUES IN (4,5,6),
  PARTITION p_big VALUES IN (9999)
);

这样租户9999的所有数据落在p_big分区,查询时优化器直接定位分区,索引结构压力也更小。

写入放大与索引数量的权衡

每多一个复合索引,写入时就要多维护一棵B+树,INSERT和UPDATE变慢,磁盘占用增加。针对tenant_id的复合索引一般一到两个就够了,不要给每张表建五六个组合。建议先梳理业务查询模式,把最高频的租户加时间、租户加状态两类查询分别建索引,其余低频查询容忍稍慢或使用联合条件。

此外,定期用pt-index-usage或数据库自带的performance_schema观察哪些索引长期不被使用,及时删除。多租户系统的索引优化不是一次性工作,而应随业务形态调整。

总结性实践清单

把上面内容浓缩成可落地的动作:

  • 所有多租户表必须有tenant_id,且复合索引首列就是它
  • 按真实查询顺序设计后续列,优先覆盖过滤与排序字段
  • 用EXPLAIN验证type与Extra,确认走索引和覆盖
  • 个别大租户用分区或独立schema隔离
  • 控制索引总数,定期清理无用索引

只要围绕tenant_id把复合索引的顺序、覆盖字段和物理分布设计好,多租户MySQL系统就能在单实例上支撑相当可观的数据规模与并发。

mysql多租户复合索引修改时间:2026-07-31 18:21:33

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。