【问题标题】:Rails multitenant architecture, scoping access to multiple tenantsRails 多租户架构,限定对多个租户的访问
【发布时间】:2013-06-24 15:23:01
【问题描述】:

我们目前有一个单租户数据库架构,MySQL 运行超过 100 个数据库。我们使用 Apartment gem 切换子域上的数据库连接,一切都很好!

但是,我们现在需要创建所谓的“伞式”客户端,这些客户端可以访问我们现有客户端组的所有数据。我不认为这对于我们的单租户数据库架构来说是立即可行的(我研究了它并查询多个 MySQL 数据库似乎很糟糕),所以我开始研究 Postgres 架构的不同实现。

我正在寻求一些建议:

  • 是否可以在 Postgres 中查询多个模式并以某种方式整理结果(寻找 Rails 实现)?我可以预见主键冲突的问题吗?

  • 有一个新的模式会更好吗? 表示/复制需要的模式组中的所有数据 被访问?它需要是实时的。

  • 如果是这样,可以在我当前的多个数据库中实现类似的东西吗 用 MySQL 设置? (尽量减少痛苦)

我对使用数据库字段在 MySQL 中实现多租户持谨慎态度,因为数据安全/隐私对于这个产品来说是一件大事,而且这样很可能会导致开发人员出错。

【问题讨论】:

  • 可能相关的线程在这里:*.com/questions/6641653/…
  • 塔恩克斯。我非常清楚如何在 Rails 中实现多租户应用程序,这是拥有一个可以访问多个其他租户的“伞形”租户的概念,这是我苦苦挣扎的地方。
  • 多个 Postgres 模式比多个 MySQL 数据库要好得多。您应该能够查询您有权访问的数据库中的任何模式,无需显式切换。我希望有人能通过股票宝石得到答案。
  • @MikeCampbell:如果你对我两年前的问题有一个很好的答案,请分享。 :-)
  • 我在我的伪多租户应用程序中所做的是我已经设置了一个模型,该模型存储了可从应用程序本身内部编辑的可配置范围,每个控制器都将其用作授权范围并过滤掉每个用户/group 我不想让他们看到的东西。

标签: mysql ruby-on-rails postgresql rails-postgresql multi-tenant


【解决方案1】:

尽管在我第一次开始做 Rails 应用程序时发现了很多多租户的例子,但我也找不到一个让我感觉完全舒服的例子。但我终于有了一个令我满意的解决方案。

我从“带范围的多租户”railscast 开始

http://railscasts.com/episodes/388-multitenancy-with-scopes

然后查看使用本指南使多租户与设计子域一起工作:

https://github.com/plataformatec/devise/wiki/How-To:--Isolate-users-to-log-into-a-single-subdomain

但我并没有从表面上看它;我深入研究以真正了解设计是如何以这种方式工作的。

一切就绪后,我就为多租户 gem 做好了准备:

https://github.com/wireframe/multitenant

但我并没有就此止步。多租户 gem 要求您在想要适当范围内的事物时说 Multitenant.with_tenant,所以我创建了一个看起来像这样的 TenantController:

  around_filter :scope_current_tenant

  def scope_current_tenant
    begin
      Firm.current = Firm.find_by_subdomain!(request.subdomain)
    rescue
      raise ActionController::RoutingError.new('Not Found')
    end

    Multitenant.with_tenant Firm.current do
      yield
    end

    ensure
      Firm.current = nil
    end
  end

然后我希望租户范围内的任何控制器都继承自 TenantController 而不是 ApplicationController。这样我就不必记住控制器细节中的任何内容,它“正常工作”。开发人员唯一需要考虑的是“这是处理租户数据的控制器吗?”

虽然这仍然取决于开发人员正确地做一些事情(从正确的控制器继承,在模型中说“acts_as_multitenant”,但它在实践中效果很好。

【讨论】:

  • 这对于我对范围多租户的担忧很有价值,感谢您的意见。
【解决方案2】:

只是从高层次上思考这个问题。

您可以创建一个 pg view 表来访问这些数据(尽管它比访问数据库本身要慢)。

然后,您希望表中有足够的唯一字段来创建复合键或复合键。 (那么您不必创建新的键列,只需创建一个索引)。因为 Rails 3 与 ORM 无关,所以您可以使用 DataMapper(或者可能是新的 ROM gem)为这个模型建立连接。

如果您使用复合键,请意识到您可能必须在模型中显式定义 *to_param* 方法才能将键构建为字符串。这是为了在您将 :id 发送到 url 时对其进行解包。

您可以通过不同的 Postgres 用户设置对该视图的访问,然后使用 Rails 的多个连接功能为其创建模型。我们之前这样做是为了聚合来自多个表的数据,但对显示的内容有一些限制,但我不明白为什么这不适用于您的用例。

另一种选择是,也许您可​​以使用 Mongo 作为“瞬态查询数据库”。 BSON 会自动为您提供唯一的密钥。您可以创建本质上是 SQL 标量对象的对象。虽然不确定在这种情况下您是否想要回写原始数据库......但您可以做到这一点。

底线 IMO 是我认为最好的解决方案在于房子的数据库方面,因为您正在使用多个数据库。在数据库层处理这些项目似乎是最好的解决方案。

说了这么多……这似乎也是一种过程气味。如果我正确阅读了问题,我认为您在这种情况下真正想要做的是 Hadoop 的设计目的......本质上是相关数据的映射/减少(又名大数据分析)

祝你好运!

【讨论】:

  • 这是我希望得到的答案,值得深思。鉴于这对我们来说是一件相当长期的事情,我不会在接受答案之前四处闲逛看看我们做了什么,这个得到了最准确地解决我的问题的大胖子。谢谢!
【解决方案3】:

OBS:我不是 Ruby 人,所以我只能给你 PG 方面的想法。

使用 PostgreSQL 架构,您可以轻松管理它。只需为您拥有的每个租户创建一个架构,因此在您的应用程序中,当您需要更改您刚刚执行的租户时:

SET search_path TO client1;

这样,当你查询一个表时,比如说customer,你只需要在同一个连接上做:

SELECT ... FROM customer ...;

而且,当您需要查询另一个租户(不在search_path 中)时,您可以使用架构查询表:

SELECT ... FROM client2.customer ...;

您可以使用另一个架构来存储公共信息,例如public 架构,只需将其添加到search_path 的末尾即可:

SET search_path TO client1, public;

【讨论】:

  • 你没有具体回答我关于如何构建这个“伞式”应用程序来整理多个租户的数据的问题,这是我问题的症结所在。我已经知道如何很好地使用模式,但感谢您的回答!
  • 根据租户的数量,您可以轻松地UNION ALL 来自不同模式甚至 PG 继承的表。但是,对于许多租户来说,这可能会成为性能问题。另一种解决方案是在租户模式上使用tenant_id(或类似的东西)列和视图,如果这对 Rails 应用程序来说是个好主意,我可能会更好地解释。
【解决方案4】:

不想这么说,因为听起来您已经对当前系统投入了大量资金,但这听起来像是一份更适合 NoSQL 解决方案的工作。

具体来说,我正在考虑使用“无模式”键值数据库设计的 MongoDB。这将使您能够根据密钥从数据库中提取数据并在软件中对其进行排序。 MongoDB 还支持分片,让您可以在任意数量的数据库服务器上使用一个数据库,听起来它可以在您的应用程序中很好地工作。 http://www.mongodb.org/ 的文档可能值得检查。我不确定它是否完美契合,但听起来它适用于您的应用程序。

【讨论】:

  • 如果值得考虑,我会考虑的!有什么具体原因吗?我几乎没有使用 NoSQL 的经验,所以很想知道更多。
  • 从像 Postgres 这样的完整 RDBMS 切换到无模式 MongoDB 并非易事。此外,MongoDB 在这里也不是灵丹妙药,它甚至可能使保护应用程序的安全性远高于按模式或数据库进行分区。