云数据库 PostgreSQL 多租户能力

多租户实例将多个租户收拢在同一个 PostgreSQL 实例中,每个租户使用独立的数据库和账号,共享实例的 CPU、内存等资源。为防止租户之间信息泄露与资源挤占,腾讯云数据库 PostgreSQL 提供两类多租户隔离能力,均由自研扩展 tencentdb_serverless 实现。

相关产品
云数据库 PostgreSQL 产品页,含版本规格、价格与购买入口。

·视图隔离(信息隔离):在共享系统视图上启用行级安全(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 行级安全策略说明

赞 (0)
上一篇 4天前
下一篇 4天前