·视图隔离(信息隔离):在共享系统视图上启用行级安全(RLS)策略,让每个租户只能看到与本库相关的信息,避免租户之间信息泄露。
·资源隔离(数据库级 CPU 限制):按数据库设置 CPU 使用上限,超限时语句通过等待自动放慢,防止单一租户的负载挤占整个实例的 CPU 资源。
一、支持版本
| 能力 | PostgreSQL 版本 | 支持版本 |
| 自定义开启多租户能力 | PostgreSQL 17 | v17.11_r1.26及以上 |
注意:说明:
注意:你可在控制台实例详情页或执行 SHOW tencentdb_version; 查看当前实例的内核版本。
二、开启多租户能力
1. 在控制台参数设置的 shared_preload_libraries 参数中勾选 tencentdb_serverless,保存后重启实例。
2. 在控制台参数设置中将 tencentdb_multi_tenant_mode 参数设置为 on。
3. 视图隔离:见 视图隔离章节 使用教程,在租户库中启用。
4. 租户库 CPU 限制:在 postgres 库中创建 tencentdb_serverless 插件,并调用限制配额函数设置。具体见 资源隔离章节。
三、参数说明
| 参数 | 级别 | 默认值 | 说明 |
| tencentdb_multi_tenant_mode | postmaster | off | 多租户模式总开关,在控制台参数设置中修改,修改后需重启实例。 |
| shared_preload_libraries | postmaster | – | 需勾选 tencentdb_serverless,在控制台参数设置中修改,修改后需重启实例。 |
| tencentdb_multi_tenant_role_prefix | sighup | tencentdb_ | 多租户模式:管理角色的名称前缀,只有以该前缀开头的角色被视为管理身份(可远程登录、可持有 REPLICATION 权限、可行使 tencentdb_superuser 的权限),不受租户库视图隔离限制影响。 |
| tencentdb_serverless.throttle_replay_database | postmaster | postgres | 资源隔离:限制配额回放工作进程读取限制配置的数据库,默认为 postgres 库。 |
注意:说明:
四、使用方法
以下示例假设多租户实例中已有管理员 root(tencentdb_superuser 成员)、租户角色 tenant_a 及其数据库 tenant_a_db、公共只读角色 proxy_ro。
五、视图隔离
多租户实例中,租户默认可通过 pg_database、pg_roles 等系统视图看到全实例的库名、角色名等信息。视图隔离功能在共享系统表上启用行级安全(RLS)策略,让每个租户只能看到与本库相关的信息。
功能启用后,以下9个共享系统表(及其相关系统视图)被隔离,租户的可见范围如下:
| 被隔离的系统表 | 租户可见范围 |
| pg_database(pg_database 视图) | 仅当前数据库 |
| pg_authid(pg_roles、pg_user 等视图) | 本库属主角色及参数配置的额外角色 |
| pg_auth_members(pg_auth_members 视图) | 可见角色之间的成员关系 |
| pg_db_role_setting(pg_db_role_setting 视图) | 仅当前数据库的参数设置 |
| pg_shdepend(pg_shdepend 视图) | 仅当前数据库的依赖关系 |
| pg_shdescription(pg_shdescription 视图) | 仅当前数据库及可见角色的注释 |
| pg_shseclabel(安全标签视图) | 仅当前数据库及可见角色的安全标签 |
| pg_subscription(pg_subscription 视图) | 仅当前数据库的订阅 |
| pg_tablespace(pg_tablespace 视图) | 仅当前数据库使用的表空间 |
六、1. 启用隔离
管理员连接租户库,创建扩展并调用启用函数:
postgres=# \\c tenant_a_db rootYou are now connected to database "tenant_a_db" as user "root".root=> CREATE EXTENSION tencentdb_serverless;CREATE EXTENSIONroot=> SELECT tencentdb_serverless.enable_system_view_isolation();NOTICE: policy "p_pg_database" for relation "pg_catalog.pg_database" does not exist, skippingNOTICE: policy "p_pg_authid" for relation "pg_catalog.pg_authid" does not exist, skippingNOTICE: policy "p_pg_auth_members" for relation "pg_catalog.pg_auth_members" does not exist, skippingNOTICE: policy "p_pg_db_role_setting" for relation "pg_catalog.pg_db_role_setting" does not exist, skippingNOTICE: policy "p_pg_shdepend" for relation "pg_catalog.pg_shdepend" does not exist, skippingNOTICE: policy "p_pg_shdescription" for relation "pg_catalog.pg_shdescription" does not exist, skippingNOTICE: policy "p_pg_seclabel" for relation "pg_catalog.pg_shseclabel" does not exist, skippingNOTICE: policy "p_pg_subscription" for relation "pg_catalog.pg_subscription" does not exist, skippingNOTICE: policy "p_pg_tablespace" for relation "pg_catalog.pg_tablespace" does not exist, skipping enable_system_view_isolation——————————(1 row)
注意:说明:
注意:NOTICE 来自启用脚本内部的 DROP POLICY IF EXISTS,属正常提示。
七、2. 配置可见角色(可选)
租户默认只能看到自己的角色。如需让租户看到额外的公共角色(如 proxy_ro),由超级用户或管理员配置库级参数,多个角色以逗号分隔:
postgres=# ALTER DATABASE tenant_a_db SET tencentdb_multi_tenant_visible_roles = 'tenant_a,proxy_ro';ALTER DATABASE
注意:说明:
注意:仅对配置后新建的会话生效。
八、3. 验证效果
租户登录后查询系统视图,只能看到本库信息:
tenant_a=> SELECT datname FROM pg_database ORDER BY 1; datname————- tenant_a_db(1 row)tenant_a=> SELECT rolname FROM pg_roles WHERE rolname NOT LIKE 'pg\\_%' ORDER BY 1; rolname———- proxy_ro tenant_a(2 rows)tenant_a=> SELECT spcname FROM pg_tablespace ORDER BY 1; spcname———— pg_default(1 row)
九、4. 关闭隔离
管理员在目标库中执行关闭函数,租户随即恢复全局可见:
root=> SELECT tencentdb_serverless.disable_system_view_isolation(); disable_system_view_isolation——————————-(1 row)tenant_a=> SELECT datname FROM pg_database ORDER BY 1; datname————- postgres template0 template1 tenant_a_db tenant_b_db tenant_c_db(6 rows)
十、资源隔离:数据库级 CPU 限制
按数据库设置 CPU 使用上限(单位:核)。设置后,该库所有会话(含并行 worker)的 CPU 消耗被统一记账,超过上限的部分转为等待,语句自动放慢,从而防止单个租户耗尽实例 CPU 资源。
以下示例在管理库(已安装 tencentdb_serverless 扩展)中执行,限制目标为租户库 tenant_a_db。示例表 big_table(800万行)为租户库中的业务表,在租户库中创建:
postgres=# \\c tenant_a_db rootYou are now connected to database "tenant_a_db" as user "root".root=> CREATE TABLE big_table AS SELECT g FROM generate_series(1, 8000000) g;SELECT 8000000root=> ANALYZE big_table;ANALYZE
十一、1. 设置 CPU 限制
set_database_cpu_limit 的参数依次为:数据库名、CPU 下限(预留下限,当前版本暂不启用)、CPU 上限。以下将租户库限制为最多0.5核:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('tenant_a_db', 0.1, 0.5); set_database_cpu_limit————————(1 row)
限制设置后立即生效,无需重启实例。
十二、2. 查看限制配置
root=> SELECT * FROM tencentdb_serverless.resource_limit_view; database_name | min_cpu_cores | max_cpu_cores | min_mem_kilobytes | max_mem_kilobytes—————+—————+—————+——————-+——————- tenant_a_db | 0.1 | 0.5 | |(1 row)
min_mem_kilobytes、max_mem_kilobytes 为预留列,当前版本不使用。
十三、3. 查看 CPU 用量与限流情况
root=> SELECT * FROM tencentdb_serverless.get_cpu_throttle_usage('tenant_a_db'); cpu_limit | cpu_usage_rate | throttled_count | throttled_time_ms———–+—————-+—————–+——————- 0.5 | 0 | 0 | 0(1 row)
·cpu_limit:当前生效的 CPU 上限(核)。
·cpu_usage_rate:近5秒观测窗口内该库的 CPU 平均使用核数。
·throttled_count:该库自首次设置限制以来累计触发限流的次数。
·throttled_time_ms:该库自首次设置限制以来累计因限流而等待的毫秒数。
十四、4. 验证限流效果
未设置限制时,对示例表的一次全表统计耗时约0.4秒:
root=> EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big_table; QUERY PLAN—————————————————————————————————- Finalize Aggregate (actual time=409.405..410.836 rows=1 loops=1) -> Gather (actual time=409.301..410.824 rows=3 loops=1) Workers Planned: 2 Workers Launched: 2 -> Partial Aggregate (actual time=407.081..407.083 rows=1 loops=3) -> Parallel Seq Scan on big_table (actual time=0.033..247.186 rows=2666667 loops=3) Planning Time: 0.212 ms Execution Time: 410.958 ms(8 rows)
将该库上限收紧为0.1核后,同一语句的 CPU 消耗被压至上限附近,耗时变为原来的约25倍,语句正常完成、不报错:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('tenant_a_db', 0.1, 0.1); set_database_cpu_limit————————(1 row)root=> EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big_table; QUERY PLAN—————————————————————————————————— Finalize Aggregate (actual time=10422.597..10424.092 rows=1 loops=1) -> Gather (actual time=10422.498..10424.083 rows=3 loops=1) Workers Planned: 2 Workers Launched: 2 -> Partial Aggregate (actual time=10420.262..10420.264 rows=1 loops=3) -> Parallel Seq Scan on big_table (actual time=0.030..10276.123 rows=2666667 loops=3) Planning Time: 0.215 ms Execution Time: 10424.213 ms(8 rows)
限流期间查询用量视图,可看到该库的 CPU 使用被精确压在0.1核附近,并产生了限流记录:
root=> SELECT * FROM tencentdb_serverless.get_cpu_throttle_usage('tenant_a_db'); cpu_limit | cpu_usage_rate | throttled_count | throttled_time_ms———–+———————+—————–+——————- 0.1 | 0.09571135357027115 | 328 | 46545.3(1 row)
注意:说明:
注意:示例语句为并行执行(2个 worker),cpu_usage_rate 针对该库全部会话统一记账,因此整体被压在上限附近。
十五、5. 删除限制
root=> SELECT tencentdb_serverless.reset_database_limit('tenant_a_db'); reset_database_limit———————-(1 row)root=> SELECT * FROM tencentdb_serverless.resource_limit_view; database_name | min_cpu_cores | max_cpu_cores | min_mem_kilobytes | max_mem_kilobytes—————+—————+—————+——————-+——————-(0 rows)
删除后该库立即恢复不受限状态。也可调用 tencentdb_serverless.reset_all_database_limit() 一次性删除实例内所有限制。
十六、6. 常见报错
目标库不存在时报错:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('db_not_exists', 0.1, 0.5);ERROR: Database db_not_exists does not exist.CONTEXT: PL/pgSQL function tencentdb_serverless.set_database_cpu_limit(text,numeric,numeric) line 9 at RAISE
CPU 上限小于下限时报错:
root=> SELECT tencentdb_serverless.set_database_cpu_limit('tenant_a_db', 0.5, 0.1);ERROR: max_cpu_cores should larger than min_cpu_cores.CONTEXT: PL/pgSQL function tencentdb_serverless.set_database_cpu_limit(text,numeric,numeric) line 28 at RAISE
十八、Q1:新建的租户库会自动开启视图隔离吗?
A:不会。隔离按库生效,需在新库中重新创建扩展并执行启用函数。
十九、Q2:如何让租户看到额外的公共角色?
A:执行 ALTER DATABASE … SET tencentdb_multi_tenant_visible_roles,将角色加入列表,租户重连后生效。
20、Q3:BYPASSRLS 属性的角色能绕过视图隔离吗?
A:不能。仅超级用户与 tencentdb_superuser 角色成员可豁免。
21、Q4:为什么租户看不到 template0、template1?
A:隔离策略仅放行当前数据库,模板库不可见,属预期行为。
22、Q5:设置了 CPU 限制后,为什么查询变慢但没有报错?
A:属预期行为。限流通过等待放慢语句执行,语句仍会正常完成;可查询 get_cpu_throttle_usage 确认是否发生了限流。
23、Q6:CPU 限制会影响其他租户库或管理库吗?
A:不会。限制按数据库生效,仅约束被设置的库;默认的 postgres 管理库不受限流约束。
24、Q7:限流计数为什么删除限制后没有清零?
A:throttled_count 与 throttled_time_ms 为该库自首次设置限制以来的累计值,删除限制不会清零。
25、Q8:执行 set_database_cpu_limit 报错 relation … does not exist?
A:请确认在已安装 tencentdb_serverless 扩展(v1.2及以上)的数据库中执行,且扩展版本支持 CPU 限制能力(见支持版本)。
26、相关参考
PostgreSQL 行级安全策略说明