多租户实例将多个租户收拢在同一个 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 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, skippingenable_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_rotenant_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-------------postgrestemplate0template1tenant_a_dbtenant_b_dbtenant_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: 2Workers 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 msExecution 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: 2Workers 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 msExecution 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 限制能力(见支持版本)。