是否可以在 rails 中有多个数据库连接池来切换?

Nie*_*ian 13 ruby postgresql ruby-on-rails multi-tenant

一点背景

多年来,我一直在使用Apartment gem来运行多租户应用程序。现在最近需要将数据库扩展到单独的主机上,数据库服务器根本无法跟上(读取和写入都变得太多) - 是的,我将硬件扩展到最大(专用硬件、64 核、raid 10 中的 12 个 Nvm-e 驱动器、384Gb ram 等)。

我正在考虑按租户执行此操作(1 个租户 = 1 个数据库连接配置/池),因为这将是一种“简单”且有效的方法,可以在number-of-tenants不进行大量应用程序代码更改的情况下获得更多的容量。

现在,我正在运行 rails 4.2 atm.,很快升级到 5.2。我可以看到 rails 6 添加了对每个模型连接定义的支持,但这并不是我真正需要的,因为我为我的 20 个租户中的每个租户都有一个完全镜像的数据库模式。通常我会根据请求(在中间件中)或每个后台作业(sidekiq 中间件)切换“数据库”,但是目前这很简单,并且可以在 Apartment gem 中处理,因为它只是search_path在 Postgresql 中设置了并且并没有真正改变实际连接。当切换到每租户托管策略时,我需要根据请求切换整个连接。

问题:

  1. 我知道我可以执行ActiveRecord::Base.establish_connection(config)每个请求/后台工作 - 但是,正如我所理解的,这会触发一个全新的数据库连接握手,并在 Rails 中生成一个新的数据库池 - 对吗?我想这将是性能自杀,在对我的应用程序的每个请求上都产生这种开销。
  2. 因此,我想知道是否有人可以看到带有 Rails 的选项,例如从一开始(例如在应用程序启动时)预先建立多个(总共 20 个)数据库连接/池,然后根据请求在这些池之间切换?这样他的数据库连接就已经建立并可以使用了。
  3. 这一切只是一个糟糕的主意吗,我应该寻找不同的方法吗?例如,1 个应用程序实例 = 到一个特定租户的一个特定连接。或者是其他东西。

KSD*_*tra 6

据我了解,多租户应用程序有 4 种模式:

1. 专用模型/多生产环境

每个实例或数据库实例完全托管不同的租户应用程序,租户之间不共享任何内容。

这是 1 个租户的 1 个实例应用程序和 1 个数据库。开发将很容易,就像您只为 1 个租户提供服务一样。但是如果你有 100 个租户,这对于 DevOps 来说将是一场噩梦。

2. 租户的物理隔离

适用于所有租户的 1 个实例应用程序,但适用于 1 个租户的 1 个数据库。这就是你正在寻找的。您可以使用ActiveRecord::Base.establish_connection(config),或使用 gems,或按照其他建议更新到 Rails 6。请参阅下面 (2) 的答案。

3. 隔离模式模型/逻辑隔离

在隔离模式中,租户表或数据库组件在逻辑模式或名称空间下分组,并与其他租户模式分开,但模式托管在同一数据库实例中。

适用于所有租户的 1 个实例应用程序和 1 个数据库,就像您在公寓 gem 中所做的那样。

4. 部分隔离组件

在这个模型中,具有共同功能的组件在租户之间共享,而具有独特或不相关功能的组件被隔离。在数据层,公共数据(例如识别租户的数据)被分组或保存在单个表中,而租户特定数据在表或实例层隔离。


至于(1),ActiveRecord::Base.establish_connection(config)如果您正确使用它,则不会根据请求与数据库握手。您可以在此处查看阅读此处的所有评论

至于(2),如果你不想使用establish_connection,你可以使用 gem multiverse(它适用于 rails 4.2),或者其他 gem。或者,正如其他人建议的那样,您可以更新到 Rails 6。

编辑:Multiverse gem 正在使用establish_connection. 它将附加database.yml, 并创建一个基类,以便每个子类共享相同的连接/池。基本上,它减少了我们使用establish_connection.

对于(3),答案是:

如果你没有那么多租户,并且你的应用程序非常复杂,我建议你使用 Dedicated Model 模式。因此,您选择 1 个应用程序实例 = 到一个特定租户的一个特定连接。您不必通过添加多个数据库连接来使您的应用程序更加复杂。

但是如果你有很多租户,我建议你使用租户的物理隔离或部分隔离的组件,这取决于你的业务流程。

无论哪种方式,您都必须更新/重写您的应用程序以符合新架构。