帮你快速理解、总结文档立即下载

多租户能力

最近更新时间:2026-10-20 15:47:01
我的收藏
多租户实例将多个租户收拢在同一个 PostgreSQL 实例中,每个租户使用独立的数据库和账号,共享实例的 CPU、内存等资源。为防止租户之间信息泄露与资源挤占,腾讯云数据库 PostgreSQL 提供两类多租户隔离能力,均由自研扩展 tencentdb_serverless 实现:
视图隔离(信息隔离):在共享系统视图上启用行级安全(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 库。
说明:
标注“由平台侧维护”的参数在控制台不可见,仅供平台侧配置,您无需关注。
租户库的 CPU 限制(第二章)不依赖 min_cpu_cores/max_cpu_cores 参数,全部通过 tencentdb_serverless 的 SQL 接口按库设置。

使用方法

以下示例假设多租户实例中已有管理员 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 root
You are now connected to database "tenant_a_db" as user "root".
root=> CREATE EXTENSION tencentdb_serverless;
CREATE EXTENSION
root=> SELECT tencentdb_serverless.enable_system_view_isolation();
NOTICE: policy "p_pg_database" for relation "pg_catalog.pg_database" does not exist, skipping
NOTICE: policy "p_pg_authid" for relation "pg_catalog.pg_authid" does not exist, skipping
NOTICE: policy "p_pg_auth_members" for relation "pg_catalog.pg_auth_members" does not exist, skipping
NOTICE: policy "p_pg_db_role_setting" for relation "pg_catalog.pg_db_role_setting" does not exist, skipping
NOTICE: policy "p_pg_shdepend" for relation "pg_catalog.pg_shdepend" does not exist, skipping
NOTICE: policy "p_pg_shdescription" for relation "pg_catalog.pg_shdescription" does not exist, skipping
NOTICE: policy "p_pg_seclabel" for relation "pg_catalog.pg_shseclabel" does not exist, skipping
NOTICE: policy "p_pg_subscription" for relation "pg_catalog.pg_subscription" does not exist, skipping
NOTICE: 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 root
You 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 8000000
root=> 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,将角色加入列表,租户重连后生效。

Q3:BYPASSRLS 属性的角色能绕过视图隔离吗?

A:不能。仅超级用户与 tencentdb_superuser 角色成员可豁免。

Q4:为什么租户看不到 template0、template1?

A:隔离策略仅放行当前数据库,模板库不可见,属预期行为。

Q5:设置了 CPU 限制后,为什么查询变慢但没有报错?

A:属预期行为。限流通过等待放慢语句执行,语句仍会正常完成;可查询 get_cpu_throttle_usage 确认是否发生了限流。

Q6:CPU 限制会影响其他租户库或管理库吗?

A:不会。限制按数据库生效,仅约束被设置的库;默认的 postgres 管理库不受限流约束。

Q7:限流计数为什么删除限制后没有清零?

A:throttled_count 与 throttled_time_ms 为该库自首次设置限制以来的累计值,删除限制不会清零。

Q8:执行 set_database_cpu_limit 报错 relation ... does not exist?

A:请确认在已安装 tencentdb_serverless 扩展(v1.2及以上)的数据库中执行,且扩展版本支持 CPU 限制能力(见支持版本)。

相关参考