多租户数据库分片模型:表主键与索引设计方案咨询
SaaS多租户(Multi DB-Multi-Tenant)数据库存储与索引设计建议
我正在开发一款SaaS应用,采用Multi DB-Multi-Tenant数据库模型——多租户可共享同一数据库,租户会根据使用情况或业务活动分配至不同数据库。现咨询如何存储数据以实现最优性能,以及表的主键和索引应如何设计。
我的所有数据表中均包含tenant_id字段,现有表结构如下:
租户表(Tenant Table)
tenant_id [INT] IDENTITY(1,1) NOT NULL, name [VARCHAR] NOT NULL, ... PRIMARY KEY CLUSTERED (tenant_id)
用户表(User Table)
user_id [INT] IDENTITY(1,1) NOT NULL, name [VARCHAR] NOT NULL, ... PRIMARY KEY CLUSTERED (user_id) FOREIGN KEY (tenant_id)
订单表(Order Table)
order_id [INT] IDENTITY(1,1) NOT NULL, date [DATETIME] NOT NULL, ... PRIMARY KEY CLUSTERED (order_id) FOREIGN KEY (tenant_id)
我不确定当前设计是否合理,且应用中所有查询均需携带tenant_id,以下为四种可选方案,恳请给出建议:
- 方案1:主键采用聚集索引
PRIMARY KEY CLUSTERED (order_id),并创建基于(tenant_id)的非聚集索引 - 方案2:主键采用聚集索引
PRIMARY KEY CLUSTERED (order_id),并创建基于(tenant_id, order_id)的非聚集索引 - 方案3:主键采用非聚集索引
PRIMARY KEY NON CLUSTERED (order_id),并创建基于(tenant_id, order_id)的聚集索引 - 方案4:主键采用聚集索引
PRIMARY KEY CLUSTERED (tenant_id, order_id)
方案分析与推荐
核心前提:所有查询都携带tenant_id,这是设计的核心依据。
方案1:不推荐
仅创建tenant_id的非聚集索引,查询时虽能定位到租户的订单,但非聚集索引需回表(数据按order_id物理存储),当租户订单量较大时,回表操作会带来明显性能损耗。且单一列的tenant_id索引无法覆盖带order_id的查询场景,额外键查找不可避免。
方案2:次优选择
(tenant_id, order_id)的非聚集索引能覆盖大部分带tenant_id的查询(如按租户查订单列表、按租户+订单ID查详情),避免回表。但数据物理上按order_id全局分散存储,同一租户的订单可能分布在不同数据页,批量查询时跨页读取会降低效率。
方案3:可选,但存在冗余
将聚集索引设为(tenant_id, order_id),同一租户的订单物理聚集,批量查询时IO更少。但非聚集主键order_id会导致所有非聚集索引都需存储order_id作为行定位器,增加存储空间,且不符合主键默认聚集的常规设计,提升维护复杂度。
方案4:最优推荐
直接将聚集主键设为(tenant_id, order_id),核心优势:
- 数据物理聚集:同一租户的所有订单存储在连续数据页中,批量查询(如拉取租户全部订单、按时间范围查订单)时大幅减少磁盘IO,提升速度。
- 索引无冗余:聚集索引本身就是主键,所有带
tenant_id的查询都能直接利用该索引,无需额外创建非聚集索引。 - 全局唯一性:
tenant_id + order_id组合保证主键全局唯一(不同租户可能有相同order_id,组合后无冲突),符合主键要求。
现有表结构优化建议
- 用户表建议同步调整聚集主键为
(tenant_id, user_id),让同一租户的用户数据物理聚集,适配所有查询带tenant_id的场景。 - 租户表当前设计合理,
tenant_id作为唯一标识,聚集索引按其存储符合需求。
内容的提问来源于stack exchange,提问作者Lionel Lario
相关产品推荐
相关产品推荐

