我建议在 PostgreSQL 邮件列表上搜索有关多租户设计的信息。那里有很多讨论,答案归结为“这取决于”。在保证隔离性、性能和可维护性之间存在各种权衡。
一种常见的方法是使用单个数据库,但一个schema http://www.postgresql.org/docs/current/static/ddl-schemas.html(命名空间)每个客户在每个模式中具有相同的表结构,以及所有这些模式中相同的数据的共享或通用模式。 PostgreSQL 模式就像 MySQL“数据库”,您可以跨不同模式进行查询,但默认情况下它们是隔离的。对于单独模式中的客户数据,您可以使用search_path http://www.postgresql.org/docs/current/static/ddl-schemas.html#DDL-SCHEMAS-PATH设置,通常通过ALTER USER http://www.postgresql.org/docs/current/static/sql-alteruser.html customername SET search_path = 'customerschema, sharedschema'
确保每个客户都能看到他们的数据,而且只能看到他们的数据。
为了获得额外的保护,您应该REVOKE http://www.postgresql.org/docs/current/static/sql-revoke.html ALL FROM SCHEMA customerschema FROM public
then GRANT http://www.postgresql.org/docs/current/static/sql-grant.htmlALL ON SCHEMA customerschema TO thecustomer
所以他们是唯一有权访问它的人,对他们的每张桌子都做同样的事情。然后,您的连接池可以使用具有以下功能的固定用户帐户登录no GRANT
ed 访问任何客户模式,但有权SET ROLE http://www.postgresql.org/docs/current/static/sql-set-role.html成为任何客户。 (通过为他们提供设置为 NOINHERIT 的每个客户角色的成员资格来做到这一点,因此必须通过以下方式明确声明权利:SET ROLE
)。连接应该立即SET ROLE
向其当前运营的客户提供。这将使您避免为每个客户建立新连接的开销,同时保持强大的保护,防止程序员错误导致访问错误的客户数据。只要泳池做DISCARD ALL http://www.postgresql.org/docs/current/interactive/sql-discard.html和/或一个RESET ROLE http://www.postgresql.org/docs/8.4/interactive/sql-set-role.html在将连接分发给下一个客户端之前,这将为您提供非常强大的隔离,而不会因为每个用户的单独连接而受挫。
如果您的 Web 应用程序环境没有内置像样的连接池(例如,您正在使用具有持久连接的 PHP),那么您really需要放一个良好的连接池 http://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling无论如何,在 Pg 和 Web 服务器之间放置,因为到后端的连接太多会损害你的性能。保镖 http://pgfoundry.org/projects/pgbouncer/ and PgPool-II http://www.pgpool.net/是最好的选择,并且可以轻松地完成DISCARD ALL
and RESET ROLE
在连接切换期间为您提供。
这种方法的主要缺点是维护这么多表的开销,因为您的非共享表的基本集是为每个客户克隆的。随着客户数量的增长,它会不断增加,以至于在 autovacuum 运行期间要检查的表的绝对数量开始变得昂贵,并且基于数据库中表总数进行扩展的任何操作都会变慢。如果您考虑在同一个数据库中拥有数千或数万个客户,那么这就是一个更大的问题,但我strongly建议您在提交之前使用虚拟数据对此设计进行一些扩展测试。
理想的方法可能是使用自动行级安全性控制元组可见性的单表,但不幸的是 PostgreSQL 尚不具备这一点。由于 SEPostgreSQL 添加了合适的基础设施和 API,它看起来正在路上,但它不在 9.1 中。