Vertica 多租户实现最佳实践¶
编译:JiangChong
原文:Implement Multi-Tenancy Using Vertica(2023-06-28)
📝 文章说明:本文基于 Vertica 官方 KB 原文翻译整理。原文发布于 2023 年,仅讨论了 Enterprise 模式下三种通用多租户方案(多 Schema / 单 Schema+tenant_id / 多集群),未涉及 Eon 模式。译者根据当前 Vertica 技术架构,补充了 Eon 模式专属的 Subcluster 隔离和 Sandbox 隔离两种方案,并更新了方案对比表、选型决策树和结论。新增内容位于「Eon 模式下的多租户方案」章节。
概述¶
Vertica 允许您设计和实现多租户系统,以服务多个客户。评估您的性能、存储和安全需求,选择最适合您数据设计和工作负载的模型。仔细考虑每个租户随时间所需的表数和列数。表和列会转换为编录对象。如果编录对象的存储需求增加,内存大小也会增加。此外,更大的编录会对数据库性能产生负面影响。
本文档提供了 Vertica 多租户环境的最佳实践。文档描述了为什么要实现多租户,以及五种配置 Vertica 数据库以支持多个租户的方法——其中三种是通用方案(Enterprise / Eon 模式均适用),两种是 Eon 模式专属方案,利用子集群机制实现更高的性价比。
- 关于多租户
- 多租户的优缺点
- 使用 Vertica 实现多租户(通用方案)
- 多 Schema:每个租户一个 Schema
- 单 Schema:向基表添加 tenant_id 字段
- 多集群:每个租户一个集群
- Eon 模式下的多租户方案(Eon 专属)
- Subcluster 隔离:每个租户一个子集群(计算隔离 + 存储共享)
- Sandbox 隔离:完全数据+计算隔离(快照后独立演进)
多租户简介¶
在 Vertica 多租户环境中,一个 Vertica 数据库实例支持多个租户访问数据。每个租户可以访问自己的数据集,或多个租户可以共享特定数据集。
软件即服务(SaaS)环境使用多租户。Vertica 客户拥有一个为多个客户提供服务的数据库。通常,该数据库包含多个数据集,每个数据集对应提供给每个客户(租户)的定制服务。
考虑一个示例:XYZ 公司拥有域名 www.xyz.com。 XYZ 公司有多个客户共享该网站空间,但使用自己的 URL。ABC 公司拥有 www.abc.com,DEF 公司拥有 www.def.com。XYZ 公司配置了一个 Vertica 数据库,使每个域在该数据库中拥有自己的数据集(定义为 Schema)。每个客户只能加载、操作和分析自己的数据。他们无法访问其他 Schema 中的数据。
这种配置为 XYZ 公司的客户节省了成本。XYZ 公司承担购买 Vertica 数据库和雇佣数据库管理员的成本,而不是其客户。XYZ 公司管理数据库并为其客户提供服务。
使用 Vertica 实现多租户的优缺点¶
如果多租户适合您的业务需求,请使用 Vertica 多租户。
| 优点 | 缺点 |
|---|---|
| 使用 Vertica,您可以通过以下功能创建多租户环境: - 用户定义的资源池允许租户共享内存资源。工作负载较高或有嘈杂邻居的租户需要自己的资源池。资源池无法帮助管理多个租户的 CPU、I/O 和网络需求。 - Schema 用于隔离每个租户,允许每个租户访问其指定 Schema 中的数据。 - 对象级备份可以单独备份每个租户,并按不同的时间表进行。 - 存储位置指定数据的存储位置。您可以出于 I/O 或安全目的将每个租户的数据存储在不同的存储位置。 - 如果只有一个数据库实例,只需执行一次数据库升级即可影响所有租户。 - 使用 EXPORT TO 和 COPY FROM 在 Schema 之间复制数据。这些语句支持在表级或 Schema 级复制数据。 |
如果您的环境需要 SaaS 多租户模型,请注意 Vertica 和多租户的以下问题: - 如果租户数量很大,您可能需要复杂的数据库配置,以确保没有租户资源匮乏。 - 某些 Vertica 数据库操作具有系统范围的影响,并且会绕过 Schema 隔离。例如,如果您为特定租户创建投影而 Vertica 未刷新该投影,则可能会阻碍所有租户的 Ancient History Mark (AHM)。此外,DDL 操作可能导致系统范围的冲突。添加新租户在创建数据库对象时可能会影响现有租户。 - 租户数量的增加可能导致数据库编录庞大。编录锁持有时间增加,从而减慢所有依赖编录锁的操作。 |
Vertica 中三种通用多租户方法¶
以下三种方法在 Enterprise 和 Eon 模式下均适用,是 Vertica 多租户的基础方案。每种方法都有其优缺点。Vertica 建议您仔细评估每种方法,以确定哪种方法最适合您的环境。
注意:如果你运行的是 Eon 模式,请务必同时阅读下一节「Eon 模式下的多租户方案」——Eon 的子集群机制提供了更优的隔离选项。
多 Schema:每个租户一个 Schema¶
在这种方法中,您为每个租户创建一个 Schema。您配置角色级和用户级安全性,以确保每个租户只能访问其特定数据。

适用场景¶
在真正需要多租户时使用每个租户一个 Schema 的方法。这种方法在风险、灵活性、性能和成本之间取得了平衡,适用于 Vertica 多租户环境。它易于设置和管理。由于租户工作负载可预测,您可以使用用户定义的资源池。当租户需要从其应用程序或客户端直接安全访问数据库时,此方法适用。
优缺点¶
| 优点 | 缺点 |
|---|---|
| - 每个租户拥有自己的 Schema 和表,数据库管理简单。 - Schema 相互隔离,无需手动过滤数据。 - 无限数量的 Schema、投影和系统范围的 ROS 容器数量。 - 易于单独备份和恢复每个租户的数据。 - 可以为每个租户定义单独的数据保留策略。 |
- 大量租户及其 Schema 和表会导致编录庞大。编录过大导致编录锁持有时间变长,影响所有数据库操作。 - 当每个租户有独立 Schema 时,跨所有租户的数据分析变得复杂。 - 需要为每个租户创建资源池来管理嘈杂邻居。 - 集群宕机时会影响所有租户。如果一个节点故障,另一个节点需为两个租户提供服务,从而影响数据库性能。 - 将数据分离到每个客户的表中,压缩时间比单表更长。 - 需要限制租户规模和数量,以避免编录过大问题。 - Vertica 使用横向扩展集群,并将查询分发到所有节点。这种设计限制了核心数量。例如,假设有 40 个并发线程。您必须为所有租户提供资源和连接。40 个小租户可能占用集群连接,而大租户可能需要等待很长时间才能连接。 |
单 Schema:向基表添加 tenant_id 字段¶
在这种方法中,您为每个租户分配一个唯一 ID。每个表包含一个 tenant_id 字段,并存储所有租户的数据。由于每个查询都指定了 tenant_id,租户可以访问其具有读取权限的数据。您必须在记录级或角色级实现安全过滤器,以维护跨租户的数据隐私。
以下查询返回 click_data 表中租户 ABC 的所有行:

适用场景¶
当 Vertica 作为托管租户的后端数据库时,使用单 Schema 方法。在这种方法中,同一张表存储相关记录,无论 tenant_id 如何。如果您想分析来自所有租户的所有数据,这些数据都在单张表中可用。
例如,对于 XYZ 公司,click_data 表记录每个租户每天的点击次数。您只需访问 click_data 表即可跨所有租户分析此数据。对于少于 10 个租户,通过实施 RLE 编码并使用 ORDER BY tenant_id,可以获得出色的性能。这种方法可以容纳许多租户。但是,您必须评估工作负载和单个租户编录大小的影响。
当租户工作负载使用内存小于 5 GB 的查询时,单 Schema 方法有效。位于不同地理时区的租户不需要同时使用资源。具有相同物理投影的租户可能会影响其他租户的服务级别协议 (SLA)。共享相同 Schema 和表的租户安全性较低。单 Schema 方法适用于固定应用程序,其中数据和报告由单一应用程序安全控制。
优缺点¶
| 优点 | 缺点 |
|---|---|
| - 基于角色的配置访问确保租户隐私。 - 较少的 Schema 和表设计避免了大编录,并确保较少的全局锁影响性能。 - 与每个租户拥有独立 Schema 相比,跨所有租户的数据分析更容易。 - 设计可扩展,没有架构上的租户数量上限。 - 能够以相同效率管理不同数据库占用规模的租户。 - BI 平台能够通过对所有查询使用安全过滤器来支持多租户。 - 在单张表中,由于所有租户的数据类型可比,压缩效果更好,而不是将数据分散到特定于租户的表中。 - 能够使用 tenant_id 和基于时间的表达式创建分区。- 使用 tenant_id 可以在查询执行期间实现分区裁剪,因为 tenant_id 出现在每个查询的 WHERE 子句中。分区裁剪基于日期/时间范围(如当前月份)进行评估。每个分区包含该月所有租户的所有数据。- 使用基于时间的表达式可以创建旧分区的离线归档。离线归档可以备份、删除,并在以后需要时恢复。使用基于时间的表达式可减少 Tuple Mover 操作的影响。新数据在活动分区中处理。 |
- 大量租户会导致分区过多。过多的分区会导致 ROS 反压和磁盘碎片。 - 跨分区批量加载新数据会导致 Tuple Mover moveout 问题。 - 同一张表中所有租户的数据若要使用分区,必须遵循相同的数据保留策略。 - 备份和恢复单个租户数据较为困难,因为表包含多个租户的数据。 - 管理挑战包括:为每个租户设置访问权限、创建和分配 tenant_id,以及使用 tenant_id 支持数据库操作。- 租户的查询会垄断系统资源。如果存在工作负载较重的租户,需要为每个租户创建资源池。 - 按租户许可证审核原始数据较为困难。 - 每个租户使用的磁盘空间有限。 - 租户之间可能因 Schema 变更产生冲突。 - 如果一个集群宕机,会影响所有租户。 |
多集群:每个租户一个集群¶
在这种方法中,您为每个租户设置多个集群。每个集群上运行一个 Vertica 实例。每个租户与其他租户完全隔离,并可完全访问其集群上的资源。
使用多个集群可以自动化集群配置并有效扩展集群。在云中添加新集群既简单又便宜。每个租户拥有独立集群,提供了完全的隔离。隔离租户消除了嘈杂邻居的影响。

适用场景¶
当 Vertica 托管在云中时,使用多集群方法实现多租户。当租户工作负载多样化且不可预测时,此方法较为有利。
优缺点¶
| 优点 | 缺点 |
|---|---|
| - 每个租户拥有自己的 Schema 和表,数据库管理简单。Schema 相互隔离,无需手动过滤数据。可以实现基于用户和基于角色的安全性。 - 大型编录在单个集群上也可管理。 - 每个租户独占系统资源。租户之间互不影响。 - 易于单独备份和恢复每个租户的数据。 - 每个租户拥有独立的数据保留策略。如果某个集群故障,只会影响一个租户。 |
- 需要额外的硬件基础设施成本,如网络、交换机、存储和服务器来支持多个集群。 - 系统和数据库管理成本随集群数量增加而增加。数据库管理员需要监控软件、执行备份和恢复,并在所有集群上执行软件升级。 - 多个 Vertica 实例需要重复的脚本和流程用于数据加载、ETL 和报表生成。 |
Eon 模式下的多租户方案¶
上述三种方案在 Enterprise 和 Eon 模式下均适用。但 Eon 模式的计算-存储分离架构引入了子集群(Subcluster) 机制,从根本上改变了多租户的设计空间——它提供了介于「多 Schema」和「多集群」之间的新选项:计算节点物理隔离,但存储共享。
Eon 模式为何改变多租户格局¶
Enterprise 模式下,每个节点既存数据也算数据,增加一个「集群」意味着要复制一份完整的数据存储(通过 rebalance 或新建集群)。这导致「多集群」方案虽然隔离性最强,但存储成本和管理成本都很高。
Eon 模式打破了这种耦合:
| 维度 | Enterprise 模式 | Eon 模式 |
|---|---|---|
| 数据存储 | 节点本地磁盘,每节点持有一部分数据 | 公共存储(S3/MinIO/HDFS),节点只做计算 |
| 创建新"集群" | 需要独立数据副本 + rebalance | 只需创建子集群,数据已在公共存储中 |
| 计算隔离成本 | 高(独立的存储+服务器+管理) | 低(只增加计算节点,共享存储) |
| 弹性 | 数小时~数天 | 分钟级 |
这意味着 Eon 模式天然支持一种新方案:为每个租户分配独立子集群,但所有租户共享同一份公共存储数据。
方案四:Eon Subcluster 隔离(每个租户一个子集群)¶
在这种方法中,你在一个 Eon 数据库中创建多个 secondary 子集群,每个子集群服务于一个租户。所有租户的数据存储在同一份公共存储中,但各自的查询在独立的计算节点上执行。
┌─────────────────────────────┐
│ 公共存储 (S3/MinIO/HDFS) │
│ 所有租户数据共享同一份存储 │
└──────┬──────────┬───────────┘
│ │
┌────────────┴──┐ ┌────┴──────────────┐
│ Subcluster A │ │ Subcluster B │
│ (租户 ABC) │ │ (租户 DEF) │
│ 3 节点 │ │ 2 节点 │
└───────────────┘ └────────────────────┘
↑ ↑
租户 ABC 的客户端 租户 DEF 的客户端
(通过 Routing Rule 路由) (通过 Routing Rule 路由)
适用场景¶
- 租户之间存在不可预测的工作负载,需要计算层物理隔离防止「嘈杂邻居」
- 租户数量适中(5~20),每个租户值得独立分配计算资源
- 需要弹性伸缩:在业务高峰期临时为某租户增加节点数,低谷期缩减
- 租户数据量大,但不需要完全独立的存储副本(数据隔离通过 Schema + Access Policy 实现)
- 云环境部署,计算节点按需付费
优势¶
- 存储不复制:所有子集群共享公共存储,无需为每个租户维护独立数据副本。存储成本显著低于「多集群」方案
- 计算物理隔离:每个租户的查询在独立节点上执行,CPU、内存、网络互不干扰。这比资源池隔离(仅内存维度)强大得多
- 弹性伸缩:secondary 子集群可随时启停,不影响数据库稳定性。租户 A 的业务高峰只影响子集群 A,与租户 B 完全无关
- 管理成本低:所有租户在同一个数据库实例中,只需执行一次升级、一次备份(对象级备份可选择性备份特定租户 Schema)
- 独立 SLA:每个子集群可配置不同的节点规格和数量,匹配各租户的 SLA 需求
- 部署快速:创建子集群是分钟级操作(
admintools -t db_add_subcluster),无需 rebalance
劣势¶
- 编录瓶颈仍在:所有子集群共享同一个 Catalog。租户数量过大时,编录锁争夺仍会影响所有租户
- 主子集群单点风险:primary 子集群故障会影响整个数据库(包括所有租户的子集群)。建议 primary 子集群保持 K-safe 配置
- Schema 级别的安全隔离仍需配置:子集群提供的是计算隔离而非数据隔离。如果租户 A 和 B 的数据在同一数据库中,需要通过 Schema + 权限 + Access Policy 确保数据安全
- 版本升级影响全局:数据库升级会同时影响所有租户,无法按租户灰度升级
配置示例¶
1. 创建租户专属子集群:
# 为租户 ABC 创建 3 节点的 secondary 子集群
admintools -t db_add_subcluster -d mydb \
-c sc_tenant_abc \
--hosts=node04,node05,node06 \
--is-secondary
# 为租户 DEF 创建 2 节点的 secondary 子集群
admintools -t db_add_subcluster -d mydb \
-c sc_tenant_def \
--hosts=node07,node08 \
--is-secondary
2. 配置子集群级别资源池覆盖(可选,精细控制每个租户的计算资源):
-- 为租户 ABC 子集群覆盖 General 资源池的内存限制
ALTER RESOURCE POOL general FOR SUBCLUSTER sc_tenant_abc
MEMORYSIZE '80%'
MAXMEMORYSIZE '90%'
MAXQUERYMEMORYSIZE '50GB';
3. 配置连接路由,将租户客户端定向到对应子集群:
-- 创建负载均衡组,绑定子集群
CREATE LOAD BALANCE GROUP lbg_tenant_abc
WITH SUBCLUSTER sc_tenant_abc
FILTER '0.0.0.0/0'
POLICY 'ROUNDROBIN';
-- 创建路由规则:租户 ABC 的客户端 IP 段路由到对应组
CREATE ROUTING RULE rr_tenant_abc
ROUTE '10.1.1.0/24' TO LOAD BALANCE GROUP lbg_tenant_abc;
从 Vertica 23.3 开始,还可以使用
WORKLOAD_ROUTING_RULES按工作负载类型(--workload参数)路由,而不依赖客户端 IP。例如:CREATE WORKLOAD ROUTING RULE wrr_etl ROUTE WORKLOAD 'etl' TO SUBCLUSTER sc_etl;
4. 弹性伸缩操作(子集群启停):
-- 租户 ABC 业务低谷期,优雅关闭其子集群(等待活跃会话排空,超时 300 秒)
SELECT SHUTDOWN_WITH_DRAIN('sc_tenant_abc', 300);
-- 或立即强制关闭(不等待活跃会话)
SELECT SHUTDOWN_SUBCLUSTER('sc_tenant_abc');
⚠️
SHUTDOWN_SUBCLUSTER()仅适用于 secondary 子集群;对 primary 子集群执行会导致数据库进入只读状态。建议生产环境优先使用SHUTDOWN_WITH_DRAIN()优雅关闭。
方案五:Eon Sandbox 隔离(完全数据+计算隔离)¶
Sandbox 是 Eon 模式的独特功能:将一个 secondary 子集群「沙箱化」,创建时继承主集群的 Catalog 和数据快照,之后沙箱内的数据变更与主集群完全隔离,互不影响。
创建 Sandbox 时
主集群 Catalog + 数据 ──快照──→ Sandbox 子集群
│
之后各自独立演进 │
主集群:DROP TABLE ➔ 不影响 ──→ Sandbox:表仍存在
主集群:LOAD 新数据 ➔ 不影响 ──→ Sandbox:看不到新数据
Sandbox:DELETE 数据 ➔ 不影响 ──→ 主集群:数据仍在
适用场景¶
- 版本升级验证:先 sandbox 一个子集群出来,在沙箱中升级并测试,确认无问题后再对主集群执行升级
- 数据共享给外部团队:将数据快照 sandbox 出去,外部团队可在沙箱中自由操作(删表、改数据、加载新数据)而不会污染主集群
- 开发/测试环境:从生产数据库 sandbox 出一份数据,开发测试结束后可直接丢弃,无需单独维护测试集群
- 需要数据完全隔离的租户:某些租户要求「我的数据和你的数据物理上不在同一份存储中」,Sandbox 可以提供比 Subcluster 更强的隔离保证
优势¶
- 数据完全隔离:Sandbox 后各自的数据变更互不影响。租户删除表、修改数据都不会传播到主集群或其他 Sandbox
- 单数据库管理:仍在同一数据库实例中,无需管理多个独立数据库
- 快速创建:Sandbox 创建是快照操作,比新建一个独立数据库快得多
- 可逆操作:Sandbox 可以 unsandbox 回到主集群(前提是沙箱中的数据变更可以被接受或放弃)
劣势¶
- 存储成本增加:Sandbox 后数据独立演进,公共存储中会逐渐产生两份数据
- 数据同步困难:Sandbox 后不再自动同步。如果主集群数据更新了,沙箱不会感知
- Sandbox 数量有限:大量 Sandbox 意味着大量独立数据副本,存储成本和管理复杂度都会上升
- Catalog 独立演进:DDL 变更不会跨 Sandbox 传播。如果需要在所有租户中同步 Schema 变更,需在每个 Sandbox 中单独执行
操作示例¶
# 1. 创建 Sandbox:将子集群 sandbox 化(第一个子集群用 admintools)
admintools -t sandbox_subcluster -d mydb -c sc_tenant_abc -b sand_tenant_abc
-- 2. 将其他子集群加入已有 Sandbox(SQL 函数,先在沙箱集群执行,再在主集群执行)
SELECT SANDBOX_SUBCLUSTER('sand_tenant_abc', 'sc_tenant_def', '');
以上语法来自 Vertica 26.2.x 官方文档。
SANDBOX_SUBCLUSTER()用于向已有 Sandbox 中添加额外子集群,而非创建 Sandbox 本身。
Eon 模式下的方案对比¶
| 维度 | 多 Schema | 单 Schema + tenant_id | Subcluster 隔离 | Sandbox 隔离 | 多集群 |
|---|---|---|---|---|---|
| 计算隔离 | ❌ 共享所有节点 | ❌ 共享所有节点 | ✅ 独立计算节点 | ✅ 独立计算节点 | ✅ 独立集群 |
| 存储隔离 | ❌ 同库 | ❌ 同表 | ❌ 共享公共存储 | ✅ 快照后独立 | ✅ 完全独立 |
| 存储成本 | 低 | 最低 | 低 | 中(逐渐增长) | 高(N 份) |
| 管理成本 | 低 | 低 | 中 | 中 | 高(N 个集群) |
| 数据共享/跨租户分析 | 需跨 Schema 查询 | ✅ 天然支持 | 需跨 Schema 查询 | ❌ 不可行 | ❌ 不可行 |
| 弹性伸缩 | ❌ 需集群级扩缩 | ❌ 需集群级扩缩 | ✅ 子集群级启停 | ✅ 子集群级启停 | ✅ 集群级启停 |
| 嘈杂邻居防护 | 差(仅资源池) | 差(仅资源池) | ✅ 物理隔离 | ✅ 物理隔离 | ✅ 物理隔离 |
| 适合租户数 | 10~50 | 100+ | 5~20 | 3~10 | 3~10 |
| 升级影响 | 影响所有租户 | 影响所有租户 | 影响所有租户 | 沙箱可独立升级 | 可按租户灰度 |
| Vertica 模式要求 | Enterprise / Eon | Enterprise / Eon | 仅 Eon | 仅 Eon | 均可 |
多租户方案选型决策树(更新)¶
需要多租户
├── 所有租户在同一个 Vertica 数据库实例中?
│ ├── 是
│ │ ├── 运行在 Eon 模式?
│ │ │ ├── 是
│ │ │ │ ├── 租户需要数据完全隔离(独立副本)?
│ │ │ │ │ ├── 是 → Sandbox 隔离
│ │ │ │ │ └── 否 → 租户需要计算物理隔离?
│ │ │ │ │ ├── 是 → Subcluster 隔离
│ │ │ │ │ └── 否 → 租户数量?
│ │ │ │ │ ├── < 50 → 多 Schema
│ │ │ │ │ └── 50+ → 单 Schema + tenant_id
│ │ │ └── 否(Enterprise 模式)
│ │ │ ├── 租户数量 < 50?→ 多 Schema
│ │ │ └── 租户数量 50+?→ 单 Schema + tenant_id
│ └── 否
│ └── 多集群(每个租户独立数据库)
结论¶
我们看到了五种多租户方案,覆盖了从轻量级逻辑隔离到完全物理隔离的全谱系:
- 单 Schema + tenant_id:最轻量,适合大量小租户,跨租户分析最方便,但安全和隔离最弱
- 多 Schema:经典方案,Schema 级隔离易管理,适合中小规模租户(10~50)
- Eon Subcluster 隔离(⭐ 推荐 Eon 用户优先评估):Eon 模式最佳性价比方案。计算物理隔离 + 存储共享,兼顾隔离性和成本。适合 5~20 个中等规模租户
- Eon Sandbox 隔离:数据+计算完全隔离但仍保持单数据库管理。适合需要独立数据副本、或用作测试/升级验证的场景
- 多集群:最强隔离,最高成本。适合 3~10 个大型租户、或安全合规要求极高的场景
关键选择逻辑:
- 如果你在 Enterprise 模式下,选择仍是三种:多 Schema / 单 Schema+tenant_id / 多集群
- 如果你在 Eon 模式下,强烈建议评估 Subcluster 隔离方案——它在不增加存储成本的前提下提供了计算物理隔离,是多租户场景下 Eon 架构的核心价值之一
- Subcluster 隔离可以与多 Schema 或单 Schema+tenant_id 组合使用:例如 Subcluster 提供计算隔离,Schema 提供数据隔离,Access Policy 提供行级安全——三层防护
请务必审视这些信息并评估您的数据库需求,以实施最适合您使用场景的设计。
扩展阅读¶
- Vertica 资源池最佳实践 — 多租户场景中资源隔离的核心手段
- Vertica 访问策略最佳实践 — 行级/列级安全策略实现数据隔离
- Vertica 弹性伸缩功能介绍与配置 — Eon 子集群机制的完整原理与操作指南(Subcluster / Sandbox / ECS)
- Vertica 客户端连接负载均衡配置 — 子集群级别的 Routing Rules 与 Workload Routing 配置
- Vertica Schema 组织与管理最佳实践 — Schema 规划、拆分、权限管理与多租户 Schema 设计