Rails + Postgres:正确的多租户实现方式?

3
我将使用Rails构建一个应用程序,并使用Apartment gem和PostgreSQL进行多租户处理。该应用程序将拥有用户,每个用户都有一个帐户。这意味着每个用户都有自己的PostgresSQL架构;用户表位于默认架构中。
每个用户在自己的架构中都有自己的客户列表。具有相同电子邮件的客户(基本上是相同的客户)可能会出现在多个模式中。我希望客户能够登录并查看他与之相关的所有用户。我不能将客户表放入默认/公共模式,因为它与不在默认模式中的其他表有关联。
我原以为可以在公共模式下创建客户和用户之间的链接表。此表将包含该客户的电子邮件和该用户的ID。我的问题在于,我不知道这对Rails的工作效果如何。我想要实现的是像customer.users这样的东西。
所以问题是:我应该如何解决这个问题?

1
你似乎在说有些用户是租户,而有些用户是客户。(因为客户可以登录,所以从某种意义上讲,他们也是用户。)你希望客户能够跨所有租户进行查询,因为用户和客户之间的映射存储在特定于租户的模式中。但这难道不正是多租户架构应该防止的吗? - Mike Sherrill 'Cat Recall'
@MikeSherrill'Catcall' 感谢您的回答。我花了几天时间思考,意识到在我的情况下,多租户模式带来的弊大于利。我将构建不带多租户模式的Rails应用程序。 - Robert Audi
2个回答

2
我创建了这个库来帮助我们解决这个问题。

https://github.com/Dandush03/pg_rls

目前最著名的实现是公寓宝石(来自Influitive)、ActiveRecordMultiTenant(来自Citus)和Rails 6.1方式的数据库分片。使用公寓和Rails 6.1方法处理大量模式/数据库时存在许多问题,主要是当您必须在每个租户上运行迁移时或者当您必须更改表上的默认值时,这是因为这需要很高的成本效益。而Citus的方法在长期运行中变得昂贵。幸运的是,PostgreSQL在pg V-9上提出了一个很好的解决方案,但它有一些性能问题,在pg V-10上解决了这些问题。这种方法允许您将特定的表保留在“特殊ID”后面,稍后可以使用pg新工具对其进行分区。我的方法主要集中在PG上,以及他们如何建议您实现RLS,并且我的大多数查询都使用SQL语句执行,这在处理运行迁移时的性能问题时有所帮助。我试图将最佳的Rails与最佳的pg函数混合在一起。在我的方法中更好的是,当您开始运行您的特殊pg搜索函数时,不会有任何下降,因为所有数据都是安全的并且在同一个DB下。此外,您将获得作为超级用户登录并获取统计信息的能力。
我可以继续下去,但我认为我的观点已经很清楚了,如果您愿意的话,我鼓励您去查看这个宝石(可能仍然有一些需要处理的错误,比如现在它只能从子域名中设置租户),但它确实让我的正在进行的项目变得更加容易。如果我得到更多的支持(比如),我将继续维护它并升级它成为一个更通用的工具。

是的,Postgres RLS看起来是解决行级多租户的更大陷阱的非常好的方法。我认为它是所有查询的自动化WHERE,在数据库级别定义,因此即使进行原始SQL也不会忘记。这是AWS博客中解释基本方法的好文章:https://aws.amazon.com/blogs/database/multi-tenant-data-isolation-with-postgresql-row-level-security/ - nandilugio

1
我建议区分你的用户(登录的,不属于租户的部分)和客户(被单独保留,并位于每个租户中)。用户表(可能伴随其他表一起)可以保存有关用户到模式/客户等的分配信息。我甚至不会使用外键将用户表与租户中的表链接起来,只是为了将它们真正分开。
简而言之,用户表仅用于身份验证和授权。 更新:该问题描述了一种使用单独的数据库模式来处理各个租户的多租户方法。在这种设置中,我不会通过数据库外键用户客户链接在一起,并且我不会一起查询它们。只需对用户进行身份验证,然后获取分配的租户。之后切换到租户。
如果您真的想在一次运行中查询两个项目(用户和客户),我将不使用单独的模式:一个模式,创建一个租户表,并将外键放入所有其他表(客户等)中。在这种情况下,您甚至可以不使用单独的用户表,并且可以查询(单个)客户表。 更新2:回答你的问题:

你可以在PostgreSql的元数据中查询模式。
select schemaname from pg_tables where tablename = 'Customer'

这会为您提供所有带有客户表的模式。 利用这些信息,您可以动态构建一个 union select 语句:

select name from schema1.customer
union all
select name from schema2.customer
union all

[...repeat for all schemas...]

查询跨模式的所有表。您可以使用 group by 去除重复项。


我不理解你的观点... users<=>customers是多对多的关系,但是customers表位于不同的架构中。我需要Rails做的是跨越所有架构进行查询(我想),而这就是我不知道如何做的... - Robert Audi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接