我们有一个 Web 应用程序,其架构要求任何注册用户(实际上是一家公司)都应与其他用户隔离,即,我将使用相同的数据模型运行相同的 Web 应用程序,但每个客户使用不同的数据集。
因此,我们确实考虑过在 Postgres 中为每个客户创建不同的数据库。这个解决方案可以扩展到 10-20K 个数据库吗?效果如何?
有人对此有更好的解决方案吗?
提前致谢。
答案1
在低端,它基本上归结为“你能绝对说你没有共享数据吗?”与 mysql 不同,数据库是 postgresql 中的绝对边界。SELECT zip_code FROM common.city_zip WHERE city=...
如果你使用单独的数据库(至少没有),你就不能dblink
)。
如果你有任何共享数据,postgresql 的“模式”类似于 mysql 所称的“数据库”。您可以CREATE SCHEMA clienta; CREATE TABLE clienta.customer (...);
。您可以为每个客户端创建一个模式,该客户端的用户将在其搜索路径中首先拥有他们的模式,并授予权限,以便客户端 A 的用户可以访问和clienta
模式public
(及其表)。
您的问题将是,在客户端数量最多的情况下,每个表都存储为一个文件,因此无论您是为每个客户端使用一个数据库,每个客户端使用一个模式,还是使用类似的${client}_customer
表名,您都可能会遇到文件描述符限制即使每个客户端只有一个表(加上每个连接一个文件描述符),也有 10k 个客户端。当然,您可以使用 sysctl 动态调整内核的最大文件描述符数量,但是如果您第一次将每个进程的限制 (ulimit) 设置得太低,则需要重新启动 postgresql。
另一种方法是使用“一张大表”,其中有一个客户端列,用于标识该行属于哪个客户端(理想情况下,如果每个客户端都有一个用户,则按用户名标识,这会使下面的内容变得容易得多)。通过不授予客户端对此表的任何访问权限,您可以创建特定于客户端的视图(或用于session_user
标识当前客户端)。但是,不能直接通过视图进行更新。您需要定义函数来在表上插入/更新/删除(每个客户端一组函数或使用session_user
),并使用函数SECURITY DEFINER
以特殊用户的身份执行,并具有在表上插入/更新/删除的权限(注意:session_user
之所以使用是因为user
和current_user
基于当前上下文,并且在 SECURITY DEFINER 函数中,这始终是定义该函数的用户)。
性能方面,除了 fd 问题之外,我真的不知道 postgresql 中有 10,000 个数据库会发生什么,而有一个包含 10,000 个客户端数据的大表会发生什么。正确的索引设计应该可以防止大表查询速度变慢。
我要说的是,我在这里为每个客户都使用了单独的数据库(我们添加服务器以保持系统可用,根据需要将客户数据库转移到新服务器,因此我们永远不会在一台服务器上拥有 10k 个数据库)。我不得不定期从备份中恢复单个客户的数据以进行调试或由于用户错误,这对于“一个大表”设计来说绝对是一场噩梦。此外,如果您打算向客户销售产品的定制版,“一个大表”设计最终可能会限制您自定义数据模型的能力。
答案2
如果没有关于应用程序的更多详细信息,很难说您将从此设置中获得任何额外的安全性。如果每个客户端都连接到 Web 应用程序,并且 Web 应用程序和数据库之间有一个共享用户,那么您的数据隔离方式与使用单个单片数据库没有任何不同。通过正确参数化的存储过程访问数据将为您提供所需的隔离级别,而无需管理任意数量的服务器上的 10,000 多个数据库的管理难题。
我个人曾在单个数据库服务器上运行过类似的设置,只使用参数化存储过程访问单个数据库。如果您可以保证仅通过存储过程访问数据库,则结果中就不会出现数据混杂的危险。
如果您确实想继续您的设计,以下是我主要考虑的问题:
ulimit -n
主机操作系统上打开的文件描述符不足 ( )- 针对不同的查询模式调整 10,000 多个数据库
- 管理 10,000 多个具有不同安全问题的数据库(备份和潜在恢复,如果服务器发生故障,您真的想恢复 10,000 多个数据库吗?)
- 在 10,000 多个数据库中推出变更