Vertica Schema 组织与管理最佳实践¶
作者:JiangChong | 撰写时间:2026年06月
适用场景:当集群中 Schema 数量膨胀、表归属混乱、多业务线共享数据库需要隔离、或需要系统性地规划 Schema 的组织结构时,使用本文建立 Schema 管理体系。
关联文章:Vertica Schema DiskQuota 配置指南 | Vertica 多租户实现最佳实践 | Vertica 访问策略最佳实践 | Vertica 资源池最佳实践
理解全文脉络¶
本文围绕 Schema 的四个管理维度组织:组织、存储、权限、多租户。第 1 节解释 Schema 在 Vertica 中的本质和工作原理;第 2 节从全局监控视角摸底现有 Schema 状态;第 3 节按优先级诊断常见 Schema 管理问题;第 4 节给出分层解决方案(清理、拆分、权限重整、配额管控);第 5–6 节通过虚构和真实案例串联全部知识;第 7–8 节提供可粘贴的 SQL 工具箱和最佳实践清单。
- 如果你不知道从哪里开始管理 Schema,建议从第 2 节全局摸底 + 第 3 节诊断开始。
- 如果你已经有明确的 Schema 拆分需求,直接跳到第 4.3 节拆分策略。
- 如果你在搭建多租户环境,从第 4.2 节多租户模型开始,配合 Vertica 多租户实现最佳实践。
- 如果你在排查权限问题,跳到第 3.4 节权限排查 + 第 4.4 节权限管理。
第 1 节:原理理解¶
1.1 Schema 在 Vertica 中的角色¶
Schema 是 Vertica 中表、视图、投影、函数等数据库对象的第一层逻辑容器。它和传统关系型数据库(如 PostgreSQL)的 Schema 概念一致:
- 每个表必然属于某个 Schema(默认
public) - 不同 Schema 可以有同名表,互不冲突
- Schema 下的表共享该 Schema 的配额、权限和备份策略
Schema 就像文件系统中的目录——你可以把表(文件)随意扔进 public(根目录),但表多了就会混乱。Schema 给你提供了一层命名空间隔离和权限边界。
1.2 Schema 搜索路径(search_path)¶
当用户执行 SELECT * FROM orders 而不带 Schema 前缀时,Vertica 按 search_path 中定义的顺序依次查找:
-- 查看当前用户的 search_path
SELECT user_name, search_path FROM v_catalog.users WHERE user_name = CURRENT_USER();
-- 默认值通常为:"$user", public, v_catalog, v_monitor, v_internal
search_path 中的 "$user" 是一个特殊占位符——Vertica 首先查找与当前用户名同名的 Schema。如果找到则使用该 Schema 中的对象,否则依次尝试 public、v_catalog、v_monitor。
这就是为什么不带 Schema 前缀的 SQL 有时会「迷路」——你以为在查 analytics.orders,但 search_path 先找到了 public.orders(如果有同名表的话)。
关键结论:多 Schema 环境下,强烈建议 SQL 中始终使用完全限定表名(
schema.table),不依赖 search_path。search_path 只做 fallback,不应依赖它来控制数据访问安全。
1.3 Eon 模式下的对象层次:Namespace → Schema → 表¶
在 Vertica Eon 模式中,Schema 之上还有一个更高层级的容器:Namespace(v24.1.x 引入)。三者的关系是严格的树状结构:
Namespace (default_namespace) ← 顶层:定义 Shard 数量
├── Schema (analytics) ← 二层:组织表、视图、权限
│ ├── orders ← 三层:实际数据表
│ └── customers
├── Schema (finance)
│ └── transactions
└── ...
Namespace (airport) ← 另一个 Namespace,可设不同 Shard 数
├── Schema (airline)
│ └── flights
└── ...
关键事实(来源:Vertica 26.2.x 官方文档):
- Namespace 是 Schema 的父容器。每个 Schema 属于且仅属于一个 Namespace,使用三部分命名:
namespace.schema.table。 - Namespace 定义 Shard 数量。创建 Namespace 时指定
SHARD COUNT n,该 Namespace 下的所有表和投影都按此数量分片存储。 - 不同 Namespace 可以有不同 Shard 数。大表+复杂查询适用更多 Shard,小表适用较少 Shard。建议 Shard 数为节点数的倍数或因子以保证负载均衡,推荐不超过节点数的 2 倍(最大 3:1)。
- 数据库默认有一个
default_namespace。不带 Namespace 前缀的CREATE SCHEMA/CREATE TABLE自动归属到default_namespace。 - 不同 Namespace 下可以有同名 Schema,互不冲突。
1.3.1 创建和查看 Namespace¶
-- 创建 Namespace(需指定 Shard 数)
CREATE NAMESPACE airport SHARD COUNT 12;
-- 查看所有 Namespace
SELECT namespace_name, is_default, default_shard_count FROM NAMESPACES;
namespace_name | is_default | default_shard_count
-------------------+------------+---------------------
default_namespace | t | 6
airport | f | 12
(2 rows)
1.3.2 在指定 Namespace 下创建 Schema 和表¶
-- 在 default_namespace 下创建(省略 Namespace 前缀)
CREATE SCHEMA airline;
CREATE TABLE airline.flights (id INT, route VARCHAR);
-- 在 airport Namespace 下创建(三部分命名)
CREATE SCHEMA airport.airline;
CREATE TABLE airport.airline.flights (id INT, route VARCHAR);
注意:
ALTER TABLE ... SET SCHEMA只能在同一 Namespace 内移动表,不能跨 Namespace 移动。
1.3.3 Shard 与 Subscription 机制¶
理解 Namespace 必须理解它下面的物理层——Shard 和 Subscription:
| 概念 | 是什么 | 关键点 |
|---|---|---|
| Shard | 公共存储中的数据分片,Namespace 定义其数量 | 每个 Namespace 还有一个 Replica Shard(存储 UNSEGMENTED 投影元数据),存在于所有节点 |
| Primary Subscriber | 每个 Shard 有一个主订阅节点,负责规划该 Shard 的 Tuple Mover 操作 | 可委托其他节点执行,不一定要自己跑 TM |
| Subscription | 节点对 Shard 的订阅关系 | K-safety≥1 时每个 Shard 在每个 Subcluster 中有多个订阅节点 |
| Critical Node | 某个 Shard 的唯一订阅节点 | 该节点宕机 → 数据库进入 READONLY 模式 |
故障行为:
- 主订阅节点宕机 → Vertica 自动从备用订阅节点选出新的 Primary Subscriber
- 某个 Shard 的全部订阅节点宕机 → 数据库进入 READONLY 模式以保护数据完整性
1.3.4 Namespace 的限制¶
RESHARD_DATABASE()只能重分片default_namespace。存在非默认 Namespace 时操作失败。- Flex Table 只能创建在
default_namespace。 - 第三方工具如果不支持三部分命名(
namespace.schema.table),使用非默认 Namespace 会导致查询失败。如果工作负载严重依赖此类工具,建议避免使用非默认 Namespace。 - VBR 备份恢复中,目标 Namespace 必须与源 Namespace 有相同的 Shard 数量和节点订阅关系。
Enterprise 模式没有 Namespace 概念——上述内容仅适用于 Eon 模式。Enterprise 模式下,Schema 直接位于数据库之下,没有这一中间层。
来源:Vertica 26.2.x 官方文档 Shards and Subscriptions + Managing Namespaces;系统表字段来源 NAMESPACES(v24.1.x 新增)。
1.4 Schema 级别权限:两种机制,别混用¶
Vertica 中 Schema 级别的权限管理有两个独立机制,容易被混淆:
| 机制 | 语法 | 作用于 | 对未来新建表 |
|---|---|---|---|
| 批量授权 | GRANT SELECT ON ALL TABLES IN SCHEMA s TO role |
仅 Schema 中当前已有的表 | ❌ 不适用 |
| 权限继承 | ALTER SCHEMA s DEFAULT INCLUDE PRIVILEGES |
Schema 上已有的 GRANT 自动传递给新创建的表 | ✅ 核心作用 |
批量授权不等于权限继承。如果你只执行 GRANT SELECT ON ALL TABLES IN SCHEMA,之后新建的表不会自动获得 SELECT 权限——必须再次执行一遍。
1.4.1 权限继承的三层开关¶
权限继承需要三个层级都就绪,缺一不可:
数据库级: ALTER DATABASE SET DisableInheritedPrivileges = 0 ← 默认 ON
└── Schema级: ALTER SCHEMA s DEFAULT INCLUDE PRIVILEGES ← 默认 OFF
└── 对象级: 新表自动 INCLUDE / 旧表手动 ALTER TABLE INCLUDE SCHEMA PRIVILEGES
关键默认值(来源:Vertica 26.2.x 官方文档):
- 数据库级:默认
DisableInheritedPrivileges = 0(继承已启用) - Schema 级:默认关闭。必须显式执行
ALTER SCHEMA ... DEFAULT INCLUDE PRIVILEGES - 对象级:Schema 启用继承后,新表自动继承;Schema 启用前已有的旧表需逐个执行
ALTER TABLE t INCLUDE SCHEMA PRIVILEGES
1.4.2 完整操作流程¶
-- 第 1 步:确认数据库级继承已启用(默认就开着,检查即可)
-- 无直接查询方式,但可通过行为验证:Schema 级启用若报 WARNING 则 DB 级未开
-- 第 2 步:授予 Schema 级别的权限
GRANT USAGE, CREATE ON SCHEMA analytics TO analyst_role;
GRANT SELECT ON SCHEMA analytics TO analyst_role;
-- 第 3 步:对已有表逐个启用继承(只影响这一步之前创建的表)
ALTER TABLE analytics.orders INCLUDE SCHEMA PRIVILEGES;
ALTER TABLE analytics.customers INCLUDE SCHEMA PRIVILEGES;
-- 第 4 步:启用 Schema 默认继承(此后新建的表自动获得 Schema 上的 GRANT)
ALTER SCHEMA analytics DEFAULT INCLUDE PRIVILEGES;
-- 至此:已有表通过第 3 步获得继承,未来新建表通过第 4 步自动获得
1.4.3 排除特定表¶
如果 Schema 启用了默认继承,但某张表不想继承(如敏感表),创建时或事后排除:
CREATE TABLE analytics.secret_data (...) EXCLUDE SCHEMA PRIVILEGES;
-- 或事后:
ALTER TABLE analytics.secret_data EXCLUDE SCHEMA PRIVILEGES;
1.4.4 常见误区¶
- 误区 1:执行
GRANT SELECT ON ALL TABLES IN SCHEMA后新建表自动有权限 → ❌ 不会,只对当前已有表生效 - 误区 2:执行
ALTER SCHEMA DEFAULT INCLUDE PRIVILEGES后已有表自动获得 Schema 权限 → ❌ 不会,只对之后新建表生效。已有表需ALTER TABLE ... INCLUDE SCHEMA PRIVILEGES - 误区 3:
GRANT SELECT ON SCHEMA本身就够 → ❌ 仅 Grant 不加INCLUDE SCHEMA PRIVILEGES,表级权限不会自动建立
来源:Vertica 26.2.x 官方文档 Inherited Privileges 及四个子页面。
1.5 Schema 问题来源总览¶
| 来源 | 典型表现 | 影响 |
|---|---|---|
| Schema 数量过多 | catalog 膨胀,全局锁持有时间变长 | DDL/DML 延迟增加 |
| 单 Schema 表数过多(> 1000) | 配额检查开销累积、权限管理困难 | DML 性能下降 |
| search_path 配置不当 | 查询访问了错误的表 | 数据结果异常、安全风险 |
| Schema 权限未分层设置 | 用户越权访问或连接报错 | 安全问题、操作受阻 |
| Schema 未配置 DiskQuota | 单 Schema 无限制增长 | 挤占集群存储空间 |
| 升级后 Schema 中存在系统表副本 | 查询系统表报字段不存在 | 升级后数据库异常 |
| Eon 模式 Namespace Shard 数设置不合理 | 大表 Shard 太少导致并行度不够,或小表 Shard 太多浪费订阅开销 | 查询性能下降、TM 操作效率低 |
第 2 节:系统级监控(从宏观入手)¶
2.1 查看所有 Schema 及其基础信息¶
v_catalog.schemata 列出数据库中所有 Schema:
SELECT
schema_name,
schema_owner,
schema_id,
schema_namespace_name -- Eon 模式:Schema 所属的 Namespace;Enterprise 模式下为空字符串(无 Namespace 概念)
FROM v_catalog.schemata
ORDER BY schema_name;
如何解读结果:
schema_owner:Schema 的所有者。新 Schema 的默认所有者是创建者。schema_namespace_name:Eon 模式下显示 Schema 归属的 Namespace(见 1.3 节「Eon 模式下的对象层次:Namespace → Schema → 表」);Enterprise 模式下为空字符串(无 Namespace 概念)。- 系统 Schema(
v_catalog、v_monitor、v_internal、v_txtindex)由dbadmin拥有,不应修改。 - 关注用户创建的 Schema 数量。如果总数超过 50 个且还在增长,需要评估是否需要整合。
2.2 查看各 Schema 的表数量¶
SELECT
table_schema,
COUNT(*) AS table_count,
SUM(CASE WHEN LENGTH(partition_expression) > 0 THEN 1 ELSE 0 END) AS partitioned_tables,
SUM(CASE WHEN LENGTH(partition_expression) = 0 THEN 1 ELSE 0 END) AS unpartitioned_tables
FROM v_catalog.tables
WHERE table_definition = ''
GROUP BY table_schema
ORDER BY table_count DESC;
如何解读结果:
| 指标 | 含义 | 阈值 |
|---|---|---|
table_count |
Schema 下的表总数 | > 1000 需关注。大量表共享一个 Schema 会导致配额检查开销累积、权限管理困难。来源:Vertica Schema DiskQuota 配置指南 3.4 节 — catalog_schema_diskquota_table_count: 1000 巡检阈值 |
partitioned_tables |
使用分区的表数量 | 占比低说明大部分表无法通过 DROP_PARTITIONS 高效清理 |
2.3 查看各 Schema 的存储占用¶
v_monitor.projection_storage 按 anchor_table_schema 聚合可直接得到每 Schema 的物理磁盘占用:
SELECT
ps.anchor_table_schema AS schema_name,
COUNT(DISTINCT ps.anchor_table_name) AS table_count,
SUM(ps.used_bytes) // 1024^3 AS total_used_gb,
COUNT(DISTINCT ps.projection_name) AS proj_count
FROM v_monitor.projection_storage ps
GROUP BY ps.anchor_table_schema
ORDER BY total_used_gb DESC;
如何解读结果:
total_used_gb是物理磁盘占用(含 buddy 副本),代表该 Schema 实际占用的磁盘空间。- 如果某 Schema 占集群总容量的 50% 以上,应评估是否拆分或设置 DiskQuota。
table_count远小于 2.2 中的表数 → 说明大量表没有物理数据(空表待清理,或为外部表——外部表无本地投影,不在此视图出现)。
2.4 查看所有 Schema 级别的 GRANT 权限¶
-- 注意:SCHEMA 类型的 grants,object_name 列存 Schema 名,object_schema 列为 NULL
SELECT
object_name AS schema_name,
grantee,
object_type,
privileges_description
FROM v_catalog.grants
WHERE object_type = 'SCHEMA'
AND object_name NOT IN ('v_catalog', 'v_monitor', 'v_internal', 'v_txtindex')
ORDER BY object_name, grantee;
如何解读结果:
object_type = 'SCHEMA'表示这是对 Schema 本身(而非表)的权限。注意:Schema 级别的 GRANT 将 Schema 名存储在object_name列,object_schema为空(与 TABLE 级别相反)。privileges_description列出授予的具体权限(如USAGE、CREATE*)。- 如果某个 Schema 的
grantee列表为空,表示只有 Schema owner 和 superuser 可以访问——对于多团队共享的 Schema,这可能意味着有人在报权限错误但你没注意到。
2.5 查看 Schema 权限继承¶
SELECT
principal,
object_schema,
privileges_description
FROM v_catalog.inherited_privileges
ORDER BY object_schema, principal;
如何解读结果:
inherited_privileges展示的是通过 Schema 级别 GRANT 自动继承到子对象的权限。- 如果这里列出了某个用户/角色,意味着该主体对该 Schema 下的所有表都有指定权限。
- 安全审查要点:检查是否有不应该访问该 Schema 的用户通过继承获得了权限。
第 3 节:逐步定位根因(从宏观到微观)¶
步骤 1:确认 Schema 规模和是否存在膨胀风险¶
做什么:快速判断数据库的 Schema 分布是否合理。
SQL:
SELECT
table_schema,
COUNT(*) AS table_count
FROM v_catalog.tables
WHERE table_definition = ''
GROUP BY table_schema
ORDER BY table_count DESC;
如何解读:
publicSchema 中表数量超过 500 → Schema 拆分不足的直接信号。大量表堆在public中,没有任何组织层次。- Schema 平均表数 < 20 → 可能存在过度拆分,Schema 数量过多反而增加 catalog 开销。
- 合理的状态:Schema 数量 = 业务子模块数量,单 Schema 表数控制在 100–500 之间。
如果不是则进入下一步:Schema 数量和表分布合理,但仍有问题。
步骤 2:检查 search_path 是否导致对象访问混乱¶
做什么:排查用户是否因为 search_path 访问了错误的表。
SQL:
SELECT
user_name,
search_path,
resource_pool
FROM v_catalog.users
WHERE user_name NOT IN ('dbadmin', 'pseudosuperuser')
ORDER BY user_name;
如何解读:
search_path以"$user"开头(默认行为)→ 不同用户有同名 Schema 时可能误入。search_path中public排在业务 Schema 前面 → 如果public下有同名表,会先匹配到public中的表。search_path与默认值不一致(如dbadmin被改为vsbak, public, ...)→ 说明有人手动ALTER USER ... SEARCH_PATH改过。用户 Schema 排在前面且不带"$user"占位符时,该用户所有不带前缀的查询都会优先命中那个 Schema,容易造成行为与其他用户不一致。排查时追问谁改的、为什么改、改完之后新加的业务 Schema 是否被遗忘。- 在多 Schema 环境中,始终使用
schema.table完全限定表名,不依赖 search_path 的顺序来区分同名表。调整 search_path 顺序是治标——换个用户、换个工具、换次登录就可能失效。
验证方法:以目标用户身份执行:
如果不是则进入下一步:search_path 配置正常。
步骤 3:排查 Schema 级别的权限问题¶
做什么:某用户/角色无法访问某个 Schema 下的表时,诊断权限链。
SQL:
-- 1. 检查用户被授予了哪些角色
SELECT user_name, all_roles, default_roles
FROM v_catalog.users
WHERE user_name = 'target_user';
-- 2. 检查目标 Schema 上授予了哪些权限(SCHEMA 类型的 object_schema 为 NULL,Schema 名在 object_name)
SELECT grantee, privileges_description
FROM v_catalog.grants
WHERE object_name = 'target_schema' AND object_type = 'SCHEMA';
-- 3. 检查目标 Schema 下表的权限(TABLE 类型使用 object_schema)
SELECT object_name, grantee, privileges_description
FROM v_catalog.grants
WHERE object_schema = 'target_schema' AND object_type = 'TABLE'
ORDER BY object_name, grantee;
如何解读:
- 用户缺乏
USAGE ON SCHEMA→ 用户根本无法看到该 Schema 中的任何对象。这是最常见的权限问题根因。 - 用户有 Schema 级别
SELECT但无表级别SELECT→ 检查GRANT SELECT ON ALL TABLES IN SCHEMA是否已执行。 - 用户通过角色继承权限但
default_roles中不包含该角色 → 用户登录后需手动SET ROLE rolename激活。
快速修复:
-- 授予 Schema 使用权限(前置条件)
GRANT USAGE ON SCHEMA target_schema TO analyst_role;
-- 授予 Schema 下所有表的 SELECT
GRANT SELECT ON ALL TABLES IN SCHEMA target_schema TO analyst_role;
来源:分区表上设置访问策略导致增删改报权限错误 — 真实案例中
GRANT all on schema public to broker+GRANT all on all tables in schema public to broker解决权限错误。
如果不是则进入下一步:权限配置正确但仍无法访问。
步骤 4:检查 Schema 是否存在系统表污染¶
做什么:排查是否有用户在 Schema 下创建了 Vertica 系统表的副本,导致升级或查询异常。
SQL:
-- 已验证:Vertica v26.1.0-2
-- 第 1 步:检查用户 Schema 下是否有与系统表同名的表
SELECT
t.table_schema,
t.table_name,
'可能与系统表 ' || s.table_schema || '.' || s.table_name || ' 冲突' AS conflict
FROM v_catalog.tables t
JOIN v_catalog.system_tables s ON t.table_name = s.table_name
ORDER BY t.table_schema, t.table_name;
-- 已验证:Vertica v26.1.0-2
-- 第 2 步:检查这些同名表所在 Schema 是否在某用户的 search_path 中(更危险)
SELECT
t.table_schema,
t.table_name,
s.table_schema AS sys_schema,
LISTAGG(DISTINCT u.user_name) AS affected_users,
'用户不带 Schema 前缀时将命中用户表而非系统表' AS risk
FROM v_catalog.tables t
JOIN v_catalog.system_tables s ON t.table_name = s.table_name
JOIN v_catalog.users u
ON INSTR(',' || REPLACE(u.search_path, ' ', '') || ',',
',' || t.table_schema || ',') > 0
GROUP BY t.table_schema, t.table_name, s.table_schema
ORDER BY t.table_schema, t.table_name;
如何解读:
- 第 1 步结果非空 → 高危。用户 Schema 下存在与系统表同名的表(如
public.nodes、public.projections),这些副本在升级后可能因缺少新版本字段导致报错。 - 第 2 步结果非空 → 紧急。同名表所在 Schema 出现在某些用户的 search_path 中(见
affected_users列),这意味着这些用户不带 Schema 前缀执行SELECT * FROM nodes时,不会命中v_catalog.nodes,而是命中这个过期的用户表副本。这比升级报错更隐蔽——不会有 error,但查询结果来自错误的数据源。 - 典型案例:
public.nodes是旧版本的v_catalog.nodes备份表,升级到 12.0.4 后因缺少sandbox字段导致停库报错(来源:从11.1.1升级到12.0.4版本后停库报sandbox错误)。
来源:从11.1.1升级到12.0.4版本后停库报sandbox错误 — 从 11.1.1 升级到 12.0.4 后,
public.nodes备份表缺少新版本sandbox字段导致停库报错。
第 4 节:解决方案(从快速见效到根本治理)¶
4.1 立即措施(当天可执行)¶
方案 A:清理 Schema 下的系统表副本¶
适用场景:发现用户 Schema 下存在 nodes、projections 等系统表副本。
-- 重命名(而非删除)以便回退
ALTER TABLE public.nodes RENAME TO public.nodes_backup_legacy;
-- 确认无报错后可删除
DROP TABLE public.nodes_backup_legacy;
为什么重命名而非直接删除:CREATE TABLE ... AS SELECT * FROM ... 创建的表是用户表,删除不会影响系统功能。但保留一段时间(1 周)以便确认无业务依赖后彻底清除。
方案 B:修正 search_path 配置¶
适用场景:用户因 search_path 顺序问题无法找到目标表。
-- 为单个用户设置 search_path(推荐:业务 Schema 在前,public 在后)
ALTER USER analyst_user SEARCH_PATH analytics, public, v_catalog, v_monitor;
-- 验证
SELECT user_name, search_path FROM v_catalog.users WHERE user_name = 'analyst_user';
为什么业务 Schema 在前:保证 SELECT * FROM orders 优先命中 analytics.orders,不会因 public 中存在同名表而导致静默错误。
方案 C:快速修复 Schema 权限¶
适用场景:用户报 permission denied for schema xxx 错误。
-- 最小权限原则:按需授予
GRANT USAGE ON SCHEMA target_schema TO role_name; -- 能看到 Schema
GRANT SELECT ON ALL TABLES IN SCHEMA target_schema TO role_name; -- 能查询所有表
GRANT CREATE ON SCHEMA target_schema TO developer_role; -- 能建表(开发角色)
注意:
USAGE ON SCHEMA是访问 Schema 下任何对象的前置条件。缺少此权限时,即使表级有SELECT权限也会报错。
4.2 多租户 Schema 设计(当周规划)¶
选择合适的租户隔离模型。Vertica 多租户实现最佳实践 详述了 5 种方案——其中 3 种通用方案(Enterprise / Eon 均适用),2 种 Eon 模式专属方案(利用子集群机制)。本文仅提供关键决策框架。
通用方案对比(3 种)¶
| 维度 | 多 Schema(每租户一Schema) | 单 Schema + tenant_id | 多集群(每租户一集群) |
|---|---|---|---|
| 隔离强度 | 🟡 中等(Schema 级别) | 🔴 较弱(需行级策略保障) | 🟢 最强(物理隔离) |
| 跨租户分析 | 🔴 复杂(需跨 Schema) | 🟢 简单(WHERE tenant_id=) | 🔴 需联邦查询 |
| Catalog 膨胀风险 | 🔴 租户数多时 catalog 大 | 🟢 catalog 小 | 🟢 各集群独立 |
| 备份灵活性 | 🟢 按 Schema 独立备份 | 🔴 表含所有租户数据 | 🟢 各集群独立 |
| 资源隔离 | 🟡 需资源池配合 | 🟡 需资源池配合 | 🟢 独占硬件 |
| 运维成本 | 🟢 中等 | 🟢 较低 | 🔴 高(多集群管理) |
| 适用规模 | 10~50 个租户 | 100+ | 3~10 个 |
| 模式要求 | Enterprise / Eon | Enterprise / Eon | 均可 |
Eon 专属方案(2 种)¶
| 维度 | Subcluster 隔离(每租户一子集群) | Sandbox 隔离(数据+计算完全隔离) |
|---|---|---|
| 计算隔离 | ✅ 独立计算节点 | ✅ 独立计算节点 |
| 存储隔离 | ❌ 共享公共存储 | ✅ 快照后独立 |
| 存储成本 | 低(不复制数据) | 中(逐渐增长) |
| 弹性伸缩 | ✅ 子集群级启停 | ✅ 子集群级启停 |
| 嘈杂邻居防护 | ✅ 物理隔离 | ✅ 物理隔离 |
| 适合租户数 | 5~20 | 3~10 |
| 模式要求 | 仅 Eon | 仅 Eon |
决策建议¶
- Enterprise 模式:在多 Schema / 单 Schema+tenant_id / 多集群中三选一
- Eon 模式:优先评估 Subcluster 隔离方案——它在不增加存储成本的前提下提供计算物理隔离,是多租户场景下 Eon 架构的核心价值。Subcluster 隔离可与多 Schema 组合:Subcluster 提供计算隔离,Schema 提供数据隔离,Access Policy 提供行级安全——三层防护
- Eon 模式 + 需要数据独立副本(开发/测试、版本升级验证、数据共享给外部团队)→ Sandbox 隔离
- > 50 个小租户 + 需要跨租户分析 → 单 Schema + tenant_id
- 详细选型决策树和配置示例见 Vertica 多租户实现最佳实践
4.3 Schema 拆分策略(根本治理)¶
当单个 Schema 的表数超过 1000 或数据集中度过高时,需要系统性拆分。以下拆分维度来自 Vertica Schema DiskQuota 配置指南 4.3 节的实战经验。
按数据生命周期分层(推荐维度)¶
-- 示例 Schema 架构
analytics_ods -- 原始贴源层,短保留期(7天),配额 2TB
analytics_dwd -- 明细宽表层,中保留期(90天),配额 5TB
analytics_dws -- 汇总层,长保留期(1年),配额 3TB
analytics_dim -- 维度表,永久保留,配额 500GB
analytics_archive -- 归档区,外部表为主,无配额限制
为什么分层:
- 故障隔离:ODS 层配额超限不影响 DWD/DWS 层写入
- 精细管控:不同保留周期的数据用不同配额,短保留周期的 ODS 层设较小配额
- 运维友好:ODS 到期数据可直接
DROP SCHEMA ... CASCADE(如果整个 Schema 都过期)或按分区批量清理 - 降低检查开销:每层表数控制在 200–500 以内,配额检查效率高
按业务子模块拆分¶
对于 ODS/DWD 层,如果单一模块表数超过 500,进一步按子模块拆分:
拆分的判断标准:
- 子模块之间有独立的 ETL 调度链 → 独立 Schema
- 子模块之间数据完全独立,无 JOIN 需求 → 独立 Schema
- 子模块之间需要频繁 JOIN → 保留在同一 Schema,不要因拆分引入跨 Schema JOIN 的复杂度
执行 Schema 拆分¶
-- 1. 创建新 Schema(带 DiskQuota)
CREATE SCHEMA IF NOT EXISTS analytics_ods_billing DISK_QUOTA '1T';
-- 2. 移动表到新 Schema(自动迁移投影和 IDENTITY 列)
ALTER TABLE original_schema.migrated_table SET SCHEMA analytics_ods_billing;
-- 3. 授权
GRANT USAGE ON SCHEMA analytics_ods_billing TO etl_role;
GRANT ALL ON ALL TABLES IN SCHEMA analytics_ods_billing TO etl_role;
注意:
SET SCHEMA需要USAGEon 源 Schema +CREATEon 目标 Schema 权限。Eon 模式下只能在同一 Namespace 内移动。
4.4 Schema 级别权限管理的最佳实践¶
权限分层模型¶
-- 第 1 层:角色定义
CREATE ROLE schema_reader; -- 只读角色
CREATE ROLE schema_writer; -- 读写角色
CREATE ROLE schema_admin; -- 管理角色(DDL)
-- 第 2 层:Schema 级别授权(两步走:现有表 + 未来表)
GRANT USAGE ON SCHEMA analytics TO schema_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA analytics TO schema_reader; -- 当前已有表
GRANT USAGE ON SCHEMA analytics TO schema_writer;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA analytics TO schema_writer;
GRANT USAGE, CREATE ON SCHEMA analytics TO schema_admin;
GRANT ALL ON ALL TABLES IN SCHEMA analytics TO schema_admin;
-- 第 3 层:启用 Schema 默认继承(此后新建表自动获得上述 GRANT)
ALTER SCHEMA analytics DEFAULT INCLUDE PRIVILEGES;
-- 第 4 层:用户分配角色
GRANT schema_reader TO analyst_user;
ALTER USER analyst_user DEFAULT ROLE schema_reader;
为什么这样分层:
- 权限变更时只需改角色,不需要逐个用户修改
- 新用户入职时直接
GRANT role TO user,权限即到位 - 审计时只需看角色→Schema 的映射关系,不枚举每个用户
GRANT ON ALL TABLES IN SCHEMA覆盖当前已有表 →DEFAULT INCLUDE PRIVILEGES覆盖未来新建表(见 1.4 节「Schema 级别权限:两种机制,别混用」)- 配合 Access Policy(Vertica 访问策略最佳实践)实现行级/列级的精细控制
第 5 节:深入案例¶
📝 虚构案例 1:public Schema 高度膨胀导致权限管理失控¶
场景描述:某金融机构 Vertica 集群运行 3 年后,public Schema 下积累了 3200 张表,来自 8 个业务团队。DB 管理员使用统一的 dbadmin 账户操作,从未设置过细粒度权限。新入职数据分析师需要访问「风控模型结果表」时,无法确定表名(public 下有 20+ 张以 risk_ 开头的表),也无法确认自己是否有权限——因为没有建过任何独立用户。
诊断过程:
-- 1. 摸底 public Schema 表数
SELECT table_schema, COUNT(*)
FROM v_catalog.tables
WHERE table_schema = 'public' AND table_definition = ''
GROUP BY table_schema;
-- 结果:public: 3200
-- 2. 按表名前缀分组,了解业务分布
SELECT
CASE
WHEN t.table_name LIKE 'risk_%' THEN '风控'
WHEN t.table_name LIKE 'traffic_%' THEN '话务'
WHEN t.table_name LIKE 'billing_%' THEN '计费'
WHEN t.table_name LIKE 'sub_%' THEN '订阅'
ELSE '其他'
END AS biz_module,
COUNT(*) AS table_count,
SUM(COALESCE(ps.used_bytes, 0)) // 1024^3 AS total_gb
FROM v_catalog.tables t
LEFT JOIN v_monitor.projection_storage ps
ON ps.anchor_table_name = t.table_name AND ps.anchor_table_schema = 'public'
WHERE t.table_schema = 'public' AND t.table_definition = ''
GROUP BY 1
ORDER BY table_count DESC;
-- 结果:话务 1200 张、计费 800 张、风控 500 张、订阅 400 张、其他 300 张
根因分析:所有团队 3 年来一直在 public Schema 下建表,没有做过任何 Schema 规划。3200 张表没有任何命名约定,表名混乱、归属不明、权限无法区分。
修复方案:
- 按业务模块创建 5 个 Schema(
risk、traffic、billing、subscribe、common) - 每 Schema 设 DiskQuota
- 按表名前缀将表迁移到对应 Schema(
risk_*→riskSchema) - 为每个业务团队创建专用角色和用户,按 Schema 授权
- 禁用
public的默认 CREATE 权限,强制新建表必须指定 Schema
效果对比:
| 指标 | 修复前 | 修复后 |
|---|---|---|
public 表数 |
3200 | 50(仅跨模块共享表) |
| 用户 Schema 数 | 0 | 5 |
| 权限管理方式 | 共享 dbadmin | 团队独享角色 + Schema |
| 新员工获得正确权限时间 | 无法(共用 dbadmin) | 5 分钟(GRANT role) |
| catalog 锁相关的 DDL 延迟 | 基线 | 降低约 30%(对象更分散) |
📝 虚构案例 2:search_path 错误导致生产报表数据静默错误¶
场景描述:某电商平台的 DBA 为数据分析团队创建了 analytics Schema,下放常用报表表。某日 BI 团队反馈「昨日 GMV 报表」数据与前日完全相同,怀疑数据加载失败。ETL 团队检查数据加载日志正常,analytics.daily_gmv 表数据已更新。但 BI 工具查询到的仍是旧数据。
诊断过程:
-- 1. 检查 BI 用户的 search_path
SELECT user_name, search_path FROM v_catalog.users WHERE user_name = 'bi_reporter';
-- 结果:bi_reporter | "$user", public, v_catalog, v_monitor
-- 2. 检查 public 下是否有同名表
SELECT table_schema, table_name
FROM v_catalog.tables
WHERE table_name = 'daily_gmv' AND table_definition = '';
-- 结果:public | daily_gmv
-- analytics | daily_gmv
-- 存在两张同名表!
-- 3. 检查 public.daily_gmv 的最后更新时间
SELECT MAX(stat_date) FROM public.daily_gmv;
-- 结果:2026-06-10(3 天前的数据,报表使用的正是这张表)
-- 4. 检查 analytics.daily_gmv 的最后更新时间
SELECT MAX(stat_date) FROM analytics.daily_gmv;
-- 结果:2026-06-14(最新的数据,从未被查询到)
根因分析:BI 用户 bi_reporter 的 search_path 以 "$user" 开头,虽然无 bi_reporter 同名 Schema,但下一个是 public。而 public 下存在一张早期创建的 daily_gmv 表(ETL 迁移到 analytics 后忘记删除),导致 SELECT * FROM daily_gmv 始终命中 public.daily_gmv。新表 analytics.daily_gmv 从未被查询到,报表数据实际停留在 3 天前,但无任何错误提示。
修复方案:
- 立即:删除
public.daily_gmv(确认无其他依赖后) - 短期:修改 BI 用户的 search_path,将
analytics放在public前面 - 长期:强制 BI 工具使用完全限定表名
analytics.daily_gmv,不依赖 search_path - 检查:扫描所有 Schema 下是否存在同名表(见第 3.2 节 SQL)
效果对比:
| 指标 | 修复前 | 修复后 |
|---|---|---|
| BI 报表数据滞后 | 3 天(静默) | 0(实时) |
public 遗留同名表 |
1 张 | 0 张 |
| SQL 使用完全限定表名 | 0% | 100% |
📋 真实案例 · Schema 污染导致升级后停库报错¶
来源:从11.1.1升级到12.0.4版本后停库报sandbox错误
客户场景:某运营商的 Vertica 集群从旧版本升级到 12.0.4。升级完成后尝试停库(admintools -t stop_db)时,操作报错退出,日志显示 Sandbox 相关字段查询失败。
诊断过程:
进一步排查发现:public Schema 下存在一张名为 nodes 的表,是升级前某次巡检脚本通过 CREATE TABLE public.nodes AS SELECT * FROM nodes 创建的备份表。旧版本 v_catalog.nodes 系统表没有 sandbox 字段,升级到 12.0.4 后,admintools 的内部查询在某些路径下匹配到了 public.nodes(而非 v_catalog.nodes),因缺少新字段而报错。
修复:
改名后停库操作恢复成功。
启示:
- 绝不要在用户 Schema(尤其是
public)下创建系统表的备份副本。使用文本导出(EXPORT TO PARQUET或 CSV)而非CREATE TABLE AS SELECT。 - 升级前应扫描所有用户 Schema,检查是否存在与系统表同名的表(见第 3.4 节)。
- 这一原则不仅适用于升级——即使不升级,同名表也可能在涉及系统表的查询中产生歧义。
📋 真实案例 · 分区表 Access Policy 权限问题¶
来源:分区表上设置访问策略导致增删改报权限错误
客户场景:在 customers_table 和 broker_info 两张分区表上配置了行级 Access Policy 后,非 manager 角色用户(user1,属于 broker 角色)无法执行 INSERT/UPDATE/DELETE 操作,报权限错误。
诊断过程:
-- 检查 broker 角色在 public Schema 上的权限
SELECT grantee, privileges_description
FROM v_catalog.grants
WHERE object_name = 'public' AND object_type = 'SCHEMA';
-- 结果:broker 角色没有 public Schema 级别的任何权限
根因:Access Policy 只能限制 SELECT 看到的数据行,但不影响 DML 权限。broker 角色缺少 Schema 级别的 INSERT/UPDATE/DELETE 权限,导致虽然能通过 Access Policy 看到数据,但无法修改。
修复:
启示:Access Policy 和 Schema 权限是两层独立的安全机制——Access Policy 控制「能看到什么」,Schema 权限控制「能做什么操作」。两者必须同时配置正确,缺一不可。
第 6 节:完整诊断流程实战¶
📝 虚构场景 · 完整演练¶
背景:某大型零售企业 Vertica 集群,3 节点 Enterprise 模式,使用 3 年。最近 DB 团队收到多个问题反馈:「查询越来越慢」「新建的表找不到」「有人能在我们的表里写数据」。决定系统性地诊断 Schema 管理状况。
时间线¶
T+0min — 全局摸底
-- 一览 Schema 全貌
SELECT
table_schema,
COUNT(*) AS table_count
FROM v_catalog.tables
WHERE table_definition = ''
GROUP BY table_schema
ORDER BY table_count DESC;
输出(截取):
table_schema | table_count
public | 1856
analytics | 342
marketing | 87
finance | 23
logistics | 15
...
判断:public Schema 占了 1856 张表,占总量的 80%。这是典型的 Schema 规划缺失——大部分业务数据堆在 public 中。
T+10min — 分析 public 的表分布
-- 按表名前缀分组
SELECT
SPLIT_PART(table_name, '_', 1) AS prefix,
COUNT(*) AS table_count
FROM v_catalog.tables
WHERE table_schema = 'public' AND table_definition = ''
GROUP BY 1
HAVING COUNT(*) > 5
ORDER BY 2 DESC;
输出:sales_(423)、inv_(312)、cust_(198)、log_(176)、report_(155)、tmp_(89)……
判断:
- 至少 5 个业务模块混在
public中 tmp_*有 89 张表 → 大量临时表未清理
T+20min — 检查权限状态
SELECT grantee, privileges_description
FROM v_catalog.grants
WHERE object_name = 'public' AND object_type = 'SCHEMA';
输出:仅 dbadmin 有权限。
判断:所有用户都在用 dbadmin 账户操作,无任何权限分层。这与「有人能在我们的表里写数据」的反馈吻合——因为根本没有权限隔离。
T+30min — 检查 search_path 配置
输出:所有用户 search_path 均为默认 "$user", public, v_catalog, v_monitor。
判断:所有用户查询不带 Schema 前缀时都先搜 public,这是「新建的表找不到」的原因——用户建了表但可能建到了其他位置或记错了表名。
T+40min — 制定修复计划
| 步骤 | 操作 | 预期效果 |
|---|---|---|
| 1 | 创建 5 个 Schema:sales、inventory、customer、logs、reports,各设 DiskQuota |
为每模块建立独立容器 |
| 2 | 按前缀迁移 public 中的表到对应 Schema |
1856 张表从 public 清空 |
| 3 | 清理 tmp_* 临时表(89 张,确认无业务依赖) |
减少 catalog 占用 |
| 4 | 创建 5 个角色 + 15 个用户,按 Schema 分配权限 | 实现权限隔离 |
| 5 | 修改 search_path,各团队默认搜自己的 Schema | 避免跨模块同名表冲突 |
| 6 | 设立命名规范:新表必须用完全限定名 schema.table |
从源头防止命名混乱 |
T+50min — 执行步骤 1-3(Schema 拆分 + 清理)
-- 创建 5 个 Schema
CREATE SCHEMA sales DISK_QUOTA '3T';
CREATE SCHEMA inventory DISK_QUOTA '2T';
CREATE SCHEMA customer DISK_QUOTA '1T';
CREATE SCHEMA logs DISK_QUOTA '2T';
CREATE SCHEMA reports DISK_QUOTA '1T';
T+90min — 执行步骤 4-6(权限 + search_path)
-- 为每个 Schema 创建角色
CREATE ROLE sales_rw; CREATE ROLE inventory_rw;
CREATE ROLE customer_rw; CREATE ROLE logs_rw; CREATE ROLE reports_rw;
-- 分配权限
GRANT USAGE ON SCHEMA sales TO sales_rw;
GRANT ALL ON ALL TABLES IN SCHEMA sales TO sales_rw;
-- ... 类似地处理其他 Schema
-- 设置 search_path
ALTER USER sales_analyst SEARCH_PATH sales, public, v_catalog, v_monitor;
ALTER USER inventory_analyst SEARCH_PATH inventory, public, v_catalog, v_monitor;
最终效果:
| 指标 | 处理前 | 处理后 |
|---|---|---|
public Schema 表数 |
1856 | 50(保留跨模块共享表) |
| 用户 Schema 数 | 4 | 9(5 个新增) |
| 权限模型 | 所有用户共用 dbadmin | 5 角色 × 15 用户,按 Schema 隔离 |
| search_path 准确率 | 全部用默认值 | 每个团队指向自己的 Schema |
| 「找不到表」反馈 | 每周 3-4 次 | 0 |
| DDL 操作平均延迟 | 基线 | 降低约 25% |
第 7 节:快速诊断 SQL 工具箱¶
| 诊断目标 | SQL | 说明 |
|---|---|---|
| 查看所有 Schema 及所有者 | SELECT schema_name, schema_owner, schema_namespace_name FROM v_catalog.schemata ORDER BY schema_name; |
Eon 模式关注 namespace_name |
| 查看各 Schema 表数量 | SELECT table_schema, COUNT(*) FROM v_catalog.tables WHERE table_definition='' GROUP BY 1 ORDER BY 2 DESC; |
> 1000 需关注 |
| 查看各 Schema 存储占用 | SELECT anchor_table_schema AS schema_name, SUM(used_bytes)//1024^3 AS used_gb FROM v_monitor.projection_storage GROUP BY 1 ORDER BY 2 DESC; |
单 Schema >50% 集群需拆分 |
| 查看 Schema 级别权限 | SELECT object_name AS schema_name, grantee, privileges_description FROM v_catalog.grants WHERE object_type='SCHEMA' AND object_name NOT IN ('v_catalog','v_monitor','v_internal','v_txtindex') ORDER BY 1,2; |
SCHEMA 类型 grants 的 object_schema 为 NULL |
| 查看 Schema 权限继承 | SELECT principal, object_schema, privileges_description FROM v_catalog.inherited_privileges ORDER BY 2,1; |
检查不期望的继承权限 |
| 查看用户 search_path | SELECT user_name, search_path FROM v_catalog.users WHERE user_name NOT IN ('dbadmin','pseudosuperuser') ORDER BY 1; |
确保业务 Schema 在 public 前 |
| 查看当前 search_path | SHOW SEARCH_PATH; |
在目标用户会话中执行 |
| 检查 Schema 下同名系统表 | SELECT t.table_schema, t.table_name FROM v_catalog.tables t JOIN v_catalog.system_tables s ON t.table_name=s.table_name ORDER BY 1,2; |
升级前必须检查 |
| 同名系统表 + 命中用户 search_path | SELECT t.table_schema, t.table_name, LISTAGG(DISTINCT u.user_name) AS affected_users FROM v_catalog.tables t JOIN v_catalog.system_tables s ON t.table_name=s.table_name JOIN v_catalog.users u ON INSTR(',' || REPLACE(u.search_path,' ','') || ',', ',' || t.table_schema || ',')>0 GROUP BY 1,2 ORDER BY 1,2; |
命中用户不带前缀时将静默查错表 |
| 查看 Schema DiskQuota 状态 | SELECT object_name, CASE WHEN is_schema THEN 'Schema' ELSE 'Table' END, disk_quota_in_bytes//1024^3 AS quota_gb, total_disk_usage_in_bytes//1024^3 AS used_gb, ROUND(total_disk_usage_in_bytes*100.0/disk_quota_in_bytes,1) AS pct FROM DISK_QUOTA_USAGES ORDER BY pct DESC; |
来自 Vertica Schema DiskQuota 配置指南 |
| 查看 Schema 下空表 | SELECT table_schema, table_name FROM v_catalog.tables WHERE table_definition='' AND table_name NOT IN (SELECT DISTINCT anchor_table_name FROM v_monitor.projection_storage) ORDER BY 1,2; |
候选清理目标 |
第 8 节:最佳实践清单¶
- 新表始终使用完全限定名
schema.table。不依赖 search_path 保证 SQL 可读性和安全性。search_path 是 fallback 机制,不应作为数据访问控制手段。当多 Schema 有同名表时,依赖 search_path 会导致静默访问错误表——没有报错、没有告警、数据就是错的。 - 新建表指定 Schema,不往
public里扔。publicSchema 的默认 CREATE 权限应被收紧(REVOKE CREATE ON SCHEMA public FROM public)。每个项目/模块应有自己的 Schema,public只放少量跨模块共享的通用表。来源:虚构案例 1 中 3200 张表堆在 public 的典型反面教材。 - 单 Schema 表数控制在 500 以内。超过 500 张表共享一个 Schema 时,DiskQuota 检查开销、权限审计复杂度、catalog 锁影响都会累积。如果业务确实需要大量表,按子模块拆分。来源:Vertica Schema DiskQuota 配置指南 巡检阈值
catalog_schema_diskquota_table_count: 1000。 - Schema 拆分按数据生命周期+业务模块两个维度。先按生命周期分层(ODS/DWD/DWS/DIM),层内按业务子模块再拆分。单层单模块表数仍超过 500 才进一步拆分。不要为了「整洁」而过度拆分——Schema 过多也会增加 catalog 管理成本。
- 权限按角色分层,不直接授予用户。创建 reader/writer/admin 三层角色,按 Schema 授权,用户通过角色获取权限。好处:新员工入职 5 分钟完成授权,离职 1 分钟回收。权限变更不需要改每个用户的配置。
- Eon 模式下优先评估 Subcluster 隔离 + 多 Schema 组合。Subcluster 提供计算物理隔离(防嘈杂邻居),多 Schema 提供数据隔离,成本远低于多集群方案。Enterprise 模式下,10-50 个租户、需要独立备份的场景首选多 Schema 模型;>50 个租户需要跨租户分析时选单 Schema + tenant_id 模型。来源:Vertica 多租户实现最佳实践(含 5 种方案完整对比和选型决策树)。
- 升级前扫描所有用户 Schema 下的系统表副本。
CREATE TABLE public.nodes AS SELECT * FROM nodes这类操作在旧版本常见,但升级后因字段变化会引发各种异常。用第 3.4 节 SQL 提前扫描。来源:从11.1.1升级到12.0.4版本后停库报sandbox错误。 - Schema DiskQuota 设置为实际用量的 1.3–1.5 倍。配额卡在刚好等于日常用量时,任何一次加载批次稍大就触发
ERROR 10764。1.3× 保证单日峰值不超限。具体操作见 Vertica Schema DiskQuota 配置指南 第 8 节。 - Access Policy 和 Schema 权限要同时配置,缺一不可。Access Policy 控制「能看到什么数据」,Schema 权限控制「能做什么操作」。配置了行级 Access Policy 但没给 Schema 级别的 INSERT/UPDATE/DELETE,用户能看到数据但无法修改。来源:分区表上设置访问策略导致增删改报权限错误。
- 建立 Schema 命名规范。Schema 名应体现
业务域_层级(如billing_ods、billing_dwd),表名体现主题_粒度(如call_detail_daily、customer_summary_monthly)。命名规范是 Schema 管理的第一道防线——当表名自解释时,search_path 的歧义风险和「找不到表」的运维问题都会大幅减少。
扩展阅读¶
- Vertica Schema DiskQuota 配置指南 — Schema 磁盘配额设置、监控与配额超限处理
- Vertica 多租户实现最佳实践 — 5 种多租户隔离方案完整对比与选型决策树
- Vertica 访问控制最佳实践 — 行级/列级访问控制与 Schema 权限的配合使用
- Vertica 资源池最佳实践 — 多 Schema 环境下的计算资源隔离