PostgreSQL 多租户 --【强人随笔 / 德哥】 原作者:digoal/德哥 创作时间:2016-11-15 00:15:36+08 |
doudou586 发布于2016-11-15 00:15:36
![]() ![]() ![]() ![]() ![]() |
PostgreSQL 多租户
作者: digoal

背景
Oracle 12c提出了数据库多租户的概念,即PDBs(私有数据库),因为早期Oracle的设计是以schema为隔离的,schema的隔离不够彻底,原因是通过赋权就很容易获得不同SCHEMA下的数据。
PDB的设计与PostgreSQL的Database概念非常相似,所以PostgreSQL实际上很适合用来实现类似PDB的场景,也即是多租户的场景。
用户可以参考我写的《PostgreSQL 逻辑结构 和 权限体系 介绍》
实际上如果不需要彻底的隔离,可以考虑继续使用schema。
使用数据库或SCHEMA作为多租户的基础,各有优劣。
基于数据库的多租户
1. 优点
数据库隔离较为彻底,从认证层面就开始隔离了,数据库与数据库之间也无法直接访问,必须要登陆到对方的数据库中才能访问对方的数据(即使使用fdw, dblink也是有登陆的过程的)。
登陆时可以通过pg_hba.conf控制来源IP,用户是否有权限登陆目标库。
同时在数据库中的权限体系中还可以配置是否允许用户访问目标库,或者在目标库创建SCHEMA。
2. 缺点
因为每个数据库对应了各自的元信息,大概有几百个文件,所以如果租户比较多,数据库也会比较多。
基于schema的多租户
1. 优点
单个数据库,多个SCHEMA的方式,比较轻巧,如果是企业私有的多租户,可以这样使用。
通过数据库的权限体系隔离用户,访问不同SCHEMA。
2. 缺点
无法通过pg_hba.conf控制schema的权限,权限隔离可能不够彻底。
用户可通过查看元表,观察到其他schema的对象,定义等信息,不安全。如果是企业内部私有的,并且这部分缺陷不敏感时可以使用。
创建租户和删除租户需要产生大量元数据,或删除大量元数据,可能导致STANDBY长时间延迟。详见 《PostgreSQL DaaS设计注意 - schema与database的抉择》
例子
基于database
--创建用户 create role 租户名 login ...; --配置防火墙 pg_hba.conf host 数据库 用户 来源IP md5 --从模板创建数据库 create database db with template templatexxx; --回收权限 revoke all on database db from public; --赋权 grant all on database db to 租户;
基于schema
--创建用户 create role 租户名 login ...; --创建schema create schema xx; --回收权限 revoke all on schema xx from public; --赋权 grant all on schema xx to 租户;
以schema为例的多租户路由选择
例如通过客户端application_name或者客户端IP地址,区分不同的租户。
每个租户的模板完全一样,只是使用了不同的schema。
客户使用search_path修改路径,完成对路由的选择。
一套程序,完成多租户的方法:
建立会话后,首先选择路由(即根据客户端IP或设置的application_name,设置对应的路由)。
也可以每次设置路由(开销大,较浪费)。
后续的操作则会自动匹配对应的schema.
路由函数举例
以application_name为schema命名
create or replace function public.route() returns void as $$ declare begin execute 'set search_path='||current_setting('application_name')||', "$user", public' ; end; $$ language plpgsql strict; postgres=# select public.route(); route ------- (1 row) postgres=# show search_path ; search_path ----------------------- psql, "$user", public (1 row)
接下来的SQL都会首先搜索psql中的对象。
如果schema很多,而且要经常调用,建议写成C function,使用更高效的匹配算法,例如hash search。
在业务函数中封装选择函数的例子。
create or replace function 业务函数(参数) returns xx as $$ declare xx; begin perform 路由函数(影响路由选择的参数); 业务SQL; //// end; $$ language plpgsql strict;

请在登录后发表评论,否则无法保存。